我相信这更多的是一个架构问题,但让我先尝试回答您的一些问题。
我的问题是,使用房间发出的默认流量,您只能
数据,所以如果我订阅该流,我只会收到
数据
如果有错误Flow
由 Room 返回,您可以通过以下方式处理catch() https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/catch.html
我需要实现的是某种方式来通知应用程序的状态,
例如加载或错误。
我同意你的观点,有一个State
对象是一个很好的方法。在我心目中,它是ViewModel
的责任是呈现State
反对View
. This State
对象应该有一种方法来暴露错误。
目前我能想到的唯一方法是一个“响应”对象
包含状态,但我似乎找不到实现它的方法。
我发现更容易拥有State
反对ViewModel
控件对错误负责,而不是从冒泡的对象Service
layer.
现在,解决了这些问题,让我尝试为您的问题提出一个特定的“解决方案”。
正如您提到的,通常的做法是Repository
处理从多个数据源检索数据。在这种情况下,Repository
会采取DAO
以及一个代表从网络获取数据的对象,我们称之为Api
。我假设你正在使用FirebaseFirestore
,所以类和方法签名看起来像这样:
class Api(private val firestore: FirebaseFirestore) {
fun getUsers() : Flow<List<UserApiPojo>
}
现在的问题是如何将基于回调的 API 变成Flow
。幸运的是,我们可以使用callbackFlow() https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/callback-flow.html为了这。然后Api
变成:
class Api(private val firestore: FirebaseFirestore) {
fun getUsers() : Flow<List<UserApiPojo> = callbackFlow {
val data = Gson().toJson(filters)
functions.getHttpsCallable("users").call(data).addOnSuccessListener {
try {
val type = object : TypeToken<List<UserApiPojo>>() {}.type
val users = Gson().fromJson<List<UserApiPojo>>(it.data.toString(), type)
offer(users.toMutableList())
} catch (e: java.lang.Exception) {
cancel(CancellationException("API Error", e))
}
}.addOnFailureListener {
cancel(CancellationException("Failure", e))
}
}
}
如你看到的,callbackFlow
允许我们在出现问题时取消流程并让下游人员处理错误。
移动到Repository
我们现在想做类似的事情:
val users: Flow<List<User>> = Flow.concat(userDao.getUsers().toUsers(), api.getUsers().toUsers()).first()
这里有一些注意事项。first()
and concat()
看来你必须想出运营商。我没看到版本first()
返回一个Flow
;它是一个终端操作员(Rx 曾经有一个版本first()
返回一个Observable
,丹·卢 (Dan Lew) 将其用于this https://blog.danlew.net/2015/06/22/loading-data-from-multiple-sources-with-rxjava/ post). Flow.concat()
似乎也不存在。目标是users
是返回一个Flow
发出任何源发出的第一个值Flows
。另请注意,我将 DAO 用户和 Api 用户映射到一个公共的User
object.
我们现在可以谈论ViewModel
。正如我之前所说,ViewModel
应该有可以容纳的东西State
. This State
应代表数据、错误和加载状态。可以实现的一种方法是使用数据类。
data class State(val users: List<User>, val loading: Boolean, val serverError: Boolean)
由于我们可以访问Repository
the ViewModel
可以看起来像:
val state = repo.users.map {users -> State(users, false, false)}.catch {emit(State(emptyList(), false, true)}
请记住,这是一个粗略的解释,为您指明方向,有很多方法可以完成状态管理,这绝不是完整的实现。将 API 调用转变为Flow
, 例如。