Jetpack Compose-使用Retrofit2结合Flow进行网络请求-下

前言

从下面可以看到使用Flow后,数据处理更加自由,如果数据处理逻辑相对复杂更推荐使用Flow。

不使用Flow

ViewModel基类

BaseViewModel.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

open class BaseViewModel : ViewModel() {
var mShowMsg = mutableStateOf(false)
var mShowMsgContent = mutableStateOf("")
var isLoading = mutableStateOf(false)

fun mLaunch(block: suspend () -> Unit) {
viewModelScope.launch(Dispatchers.IO) {
try {
block()
} catch (e: Exception) {
withContext(Dispatchers.Main) {
e.printStackTrace()
isLoading.value = false
mShowMsg.value = true
mShowMsgContent.value = "数据加载失败"
}
}
}
}
}

ViewModel实现

UserViewModel.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import android.util.Log
import androidx.compose.runtime.mutableStateListOf
import com.google.gson.Gson
import com.xhkjedu.zxs_android.api.ApiManager
import com.xhkjedu.zxs_android.model.UserItem
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class UserViewModel : BaseViewModel() {
private val TAG = "UserViewModel"
val userList = mutableStateListOf<UserItem>()

//加载分组
fun loadUserList() {
mLaunch {
val result = ApiManager.userService.getUserList().body()
Log.i("API", Gson().toJson(result))
withContext(Dispatchers.Main) {
result?.let {
Log.i(TAG, "Thread: " + Thread.currentThread().name)
Log.i(TAG, "UserViewModel: " + Gson().toJson(it))
if (it.code == 0) {
if (it.obj == null) {
it.obj = mutableListOf()
}
userList.addAll(it.obj)
mShowMsg.value = false
} else {
mShowMsg.value = true
mShowMsgContent.value = it.msg
}
}
}
}
}
}

使用Flow

ViewModel基类

BaseViewModel.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import retrofit2.Response
import java.io.IOException
import java.net.SocketException

sealed class NetworkResult<out T> {
data class Success<out T>(val data: T) : NetworkResult<T?>()
data class Error(
val message: String,
val exception: Exception? = null
) : NetworkResult<Nothing>()

object Loading : NetworkResult<Nothing>()
}

open class BaseViewModel : ViewModel() {
var mShowMsg = mutableStateOf(false)
var mShowMsgContent = mutableStateOf("")
var isLoading = mutableStateOf(false)

@Suppress("UNCHECKED_CAST")
fun <T> getFlow(reqBlock: suspend () -> Response<T>): Flow<NetworkResult<T>> {
return flow {
// 发射加载中状态
emit(NetworkResult.Loading)

try {
val result = reqBlock()
val data = result.body()
if (data != null) {
emit(NetworkResult.Success(data))
} else {
emit(NetworkResult.Error("请求数据失败"))
}
} catch (e: Exception) {
emit(NetworkResult.Error("请求数据失败"))
}
}
.catch { e ->
// 集中处理所有异常,携带具体信息
val errorMsg = when (e) {
is SocketException -> "网络连接失败"
is IOException -> "数据读取失败"
else -> "请求处理异常: ${e.message}"
}
emit(NetworkResult.Error(errorMsg))
}
.flowOn(Dispatchers.IO) as Flow<NetworkResult<T>>
}

@Suppress("UNCHECKED_CAST")
fun <T> mLaunch(
reqBlock: suspend () -> Response<T>,
successBlock: suspend (data: T) -> Unit
) {
viewModelScope.launch {
getFlow(reqBlock)
.collect { result ->
when (result) {
is NetworkResult.Loading -> {
isLoading.value = true
mShowMsg.value = false
}

is NetworkResult.Success<*> -> {
isLoading.value = false
mShowMsg.value = false
val data = result.data
data?.let {
successBlock(it as T)
}
}

is NetworkResult.Error -> {
isLoading.value = false
mShowMsg.value = true
mShowMsgContent.value = result.message
}
}
}
}
}
}

ViewModel实现

UserViewModel.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import android.util.Log
import androidx.compose.runtime.mutableStateListOf
import com.google.gson.Gson
import com.xhkjedu.zxs_android.api.ApiManager
import com.xhkjedu.zxs_android.model.UserItem

class UserViewModel : BaseViewModel() {
private val TAG = "UserViewModel"
val userList = mutableStateListOf<UserItem>()

fun loadUserListUseFlow() {
mLaunch(suspend { ApiManager.userService.getUserList() }) { data ->
Log.i(TAG, "UserViewModel: " + Gson().toJson(data))
data.obj?.let {
userList.addAll(it)
}
}
}
}

相关

数据实体

ResultVo.kt

1
2
3
4
5
data class ResultVo<T>(
val code: Int,
val msg: String,
val obj: T?
)

这个非必要,接收的时候可以使用ResultVo<Collection<UserItem>>替代。

ResultListVo.kt

1
2
3
4
5
class ResultListVo<T> {
var code: Int = 0
var msg: String = ""
var obj: MutableList<T>? = null
}

服务层

ServiceUser.kt

1
2
3
4
5
6
7
8
9
import com.xhkjedu.zxs_android.model.ResultVo
import com.xhkjedu.zxs_android.model.UserItem
import retrofit2.Response
import retrofit2.http.GET

interface ServiceUser {
@GET("userlist.json")
suspend fun getUserList(): Response<ResultVo<Collection<UserItem>>>
}

也可以这样写

1
2
3
4
5
6
7
8
9
import com.xhkjedu.zxs_android.model.ResultListVo
import com.xhkjedu.zxs_android.model.UserItem
import retrofit2.Response
import retrofit2.http.GET

interface ServiceUser {
@GET("userlist.json")
suspend fun getUserList(): Response<ResultListVo<UserItem>>
}

管理层

ApiManager.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import android.annotation.SuppressLint
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.security.SecureRandom
import java.util.concurrent.TimeUnit
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager


object ApiManager {
private lateinit var userRetrofit: Retrofit

lateinit var userService: ServiceUser


@SuppressLint("CustomX509TrustManager")
fun getUnsafeOkHttpClient(): OkHttpClient {
try {
// 创建一个信任所有证书的 TrustManager
val trustAllCerts = arrayOf<TrustManager>(

object : X509TrustManager {
override fun checkClientTrusted(
chain: Array<out java.security.cert.X509Certificate>?,
authType: String?
) {
}

override fun checkServerTrusted(
chain: Array<out java.security.cert.X509Certificate>?,
authType: String?
) {
}

override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> =
arrayOf()
}
)

// 初始化 SSLContext
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, trustAllCerts, SecureRandom())

// 创建一个允许所有主机名验证的 HostnameVerifier
val allHostsValid = HostnameVerifier { _, _ -> true }

// 创建 OkHttpClient 并配置 SSL 和主机名验证
return OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier(allHostsValid)
.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}

init {
if (!::userRetrofit.isInitialized) {
val client = getUnsafeOkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.addInterceptor(HeaderInterceptor())
.build();

userRetrofit = Retrofit.Builder()
.baseUrl("https://www.psvmc.cn/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
}

if (!::userService.isInitialized) {
userService = userRetrofit.create(ServiceUser::class.java)
}
}
}

请求头切面

HeaderInterceptor.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import android.util.Log
import com.xhkjedu.zxs_android.common.CommonData.deviceModel
import com.xhkjedu.zxs_android.common.CommonData.loginUser
import com.xhkjedu.zxs_android.utils.ZDeviceUtils
import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException

class HeaderInterceptor : Interceptor {
var TAG: String = "HeaderInterceptor"

@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val systemModel = ZDeviceUtils.getSystemModel()
Log.i(TAG, "intercept: " + systemModel)
val original = chain.request()
try {
val mBuilder = original.newBuilder()
if (loginUser != null) {
mBuilder.header("XH-UserId", loginUser!!.getUserid().toString() + "")
}

val request = mBuilder
.header("XH-DeviceModel", deviceModel())
.method(original.method, original.body)
.build()

return chain.proceed(request)
} catch (e: Exception) {
return chain.proceed(original)
}
}
}