gpt4 book ai didi

android - 使用 Retrofit + Kotlin Flow 处理错误的优雅方式

转载 作者:行者123 更新时间:2023-12-04 23:40:40 27 4
gpt4 key购买 nike

我有一种在 Android 上进行网络请求的最喜欢的方式(使用 Retrofit)。它看起来像这样:

// NetworkApi.kt

interface NetworkApi {
@GET("users")
suspend fun getUsers(): List<User>
}
在我的 ViewModel 中:
// MyViewModel.kt

class MyViewModel(private val networkApi: NetworkApi): ViewModel() {
val usersLiveData = flow {
emit(networkApi.getUsers())
}.asLiveData()
}
最后,在我的 Activity/fragment 中:
//MyActivity.kt

class MyActivity: AppCompatActivity() {
private viewModel: MyViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

viewModel.usersLiveData.observe(this) {
// Update the UI here
}
}
}
我喜欢这种方式的原因是因为它原生支持 Kotlin 流,非常好用,并且有很多有用的操作(flatMap 等)。
但是,我不确定如何使用这种方法优雅地处理网络错误。我能想到的一种方法是使用 Response<T>作为网络 API 的返回类型,如下所示:
// NetworkApi.kt

interface NetworkApi {
@GET("users")
suspend fun getUsers(): Response<List<User>>
}
然后在我的 View 模型中,我可以使用 if-else 来检查 isSuccessful的响应,并使用 .body() 获得实际结果API 是否成功。但是当我在我的 View 模型中进行一些转换时会出现问题。例如。
// MyViewModel.kt
class MyViewModel(private val networkApi: NetworkApi): ViewModel() {
val usersLiveData = flow {
val response = networkApi.getUsers()
if (response.isSuccessful) {
emit(response.body()) // response.body() will be List<User>
} else {
// What should I do here?
}
}.map { // it: List<User>
// transform Users to some other class
it?.map { oneUser -> OtherClass(oneUser.userName) }
}.asLiveData()
请注意评论“我应该在这里做什么?”。我不知道在这种情况下该怎么办。我可以用一些“状态”(或者只是通过响应本身)包装 responseBody(在这种情况下是用户列表)。但这意味着我几乎必须使用 if-else 来检查流转换链中每一步的状态,一直到 UI。如果链真的很长(例如我有 10 个 mapflatMapConcat 在链上),每一步都这样做真的很烦人。
请问在这种情况下处理网络错误的最佳方法是什么?

最佳答案

你应该有一个 sealed class处理不同类型的事件。例如,Success , ErrorLoading .这是一些适合您的用例的示例。

enum class ApiStatus{
SUCCESS,
ERROR,
LOADING
} // for your case might be simplify to use only sealed class

sealed class ApiResult <out T> (val status: ApiStatus, val data: T?, val message:String?) {

data class Success<out R>(val _data: R?): ApiResult<R>(
status = ApiStatus.SUCCESS,
data = _data,
message = null
)

data class Error(val exception: String): ApiResult<Nothing>(
status = ApiStatus.ERROR,
data = null,
message = exception
)

data class Loading<out R>(val _data: R?, val isLoading: Boolean): ApiResult<R>(
status = ApiStatus.LOADING,
data = _data,
message = null
)
}
然后,在您的 ViewModel 中,
class MyViewModel(private val networkApi: NetworkApi): ViewModel() {

// this should be returned as a function, not a variable
val usersLiveData = flow {
emit(ApiResult.Loading(true)) // 1. Loading State
val response = networkApi.getUsers()
if (response.isSuccessful) {
emit(ApiResult.Success(response.body())) // 2. Success State
} else {
val errorMsg = response.errorBody()?.string()
response.errorBody()?.close() // remember to close it after getting the stream of error body
emit(ApiResult.Error(errorMsg)) // 3. Error State
}
}.map { // it: List<User>
// transform Users to some other class
it?.map { oneUser -> OtherClass(oneUser.userName) }
}.asLiveData()
在您的 View (Activity/Fragment)中,观察这些状态。
 viewModel.usersLiveData.observe(this) { result ->
// Update the UI here
when(result.status) {
ApiResult.Success -> {
val data = result.data <-- return List<User>
}
ApiResult.Error -> {
val errorMsg = result.message <-- return errorBody().string()
}
ApiResult.Loading -> {
// here will actually set the state as Loading
// you may put your loading indicator here.
}
}
}

关于android - 使用 Retrofit + Kotlin Flow 处理错误的优雅方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67812248/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com