我有一个单独的类,负责处理数据提取(特别是Firebase),通常从中返回LiveData对象并异步更新它们。现在,我想将返回的数据存储在ViewModel中,但是问题是,为了获取所述值,我需要观察从数据获取类返回的LiveData对象。观察方法要求将LifecycleOwner对象作为第一个参数,但是我显然不在ViewModel中拥有该对象,并且我知道我不应该在ViewModel中保留对Activity / Fragment的引用。我该怎么办?
我有一个单独的类,负责处理数据提取(特别是Firebase),通常从中返回LiveData对象并异步更新它们。现在,我想将返回的数据存储在ViewModel中,但是问题是,为了获取所述值,我需要观察从数据获取类返回的LiveData对象。观察方法要求将LifecycleOwner对象作为第一个参数,但是我显然不在ViewModel中拥有该对象,并且我知道我不应该在ViewModel中保留对Activity / Fragment的引用。我该怎么办?
Answers:
在Google开发人员JoseAlcérreca的这篇博客文章中,建议在这种情况下使用一种转换(请参阅“存储库中的LiveData”段落),因为ViewModel不应保存与View
(Activity,Context等)相关的任何引用,因为这样做会使操作变得困难去测试。
在ViewModel文档中
但是,ViewModel对象绝不能观察到生命周期感知的可观察对象的更改,例如LiveData对象。
另一种方法是让数据实现RxJava而不是LiveData,那么它就不会具有生命周期感知的优势。
在Google的todo-mvvm-live-kotlin示例中,它在ViewModel中使用没有LiveData的回调。
我想如果您要遵守整个生命周期软件的概念,我们需要在“活动/片段”中移动观察代码。另外,我们可以在ViewModel中使用回调或RxJava。
另一个折衷方案是实现MediatorLiveData(或Transformations)并在ViewModel中进行观察(在此处输入您的逻辑)。注意,除非在“活动/片段”中观察到MediatorLiveData观察者,否则它不会触发(与“转换”相同)。我们要做的是在“活动/片段”中放置空白观察,其中实际工作实际上是在ViewModel中完成的。
// ViewModel
fun start(id : Long) : LiveData<User>? {
val liveData = MediatorLiveData<User>()
liveData.addSource(dataSource.getById(id), Observer {
if (it != null) {
// put your logic here
}
})
}
// Activity/Fragment
viewModel.start(id)?.observe(this, Observer {
// blank observe here
})
PS:我阅读了ViewModels和LiveData:Patterns + AntiPatterns,它建议进行转换。我认为除非观察到LiveData(否则可能需要在Activity / Fragment中完成),否则它不会起作用。
mLiveData.asFlow()
)或observeForever
。
我认为您可以使用无需生命周期所有者接口的observeForever,并且可以从viewmodel观察结果
Cannot invoke observeForever on a background thread
onCleared
。至于后台线程-从主线程观察,就是这样。
observeForever
通过GlobalScope.launch(Dispatchers.Main) { myvm.observeForever() }
您可以使用liveData
builder函数来调用suspend
函数,并将结果作为LiveData
对象。
val user: LiveData<User> = liveData {
val data = database.loadUser() // loadUser is a suspend function.
emit(data)
}
您还可以从块中发出多个值。每次emit()
调用都会暂停执行该块,直到LiveData
在主线程上设置了该值。
val user: LiveData<Result> = liveData {
emit(Result.loading())
try {
emit(Result.success(fetchUser()))
} catch(ioException: Exception) {
emit(Result.error(ioException))
}
}
在gradle配置中,使用androidx.lifecycle:lifecycle-livedata-ktx:2.2.0
或更高版本。
更新:也有可能改变LiveData<YourData>
的Dao
interface
。您需要将suspend
关键字添加到函数中:
@Query("SELECT * FROM the_table")
suspend fun getAll(): List<YourData>
在中,ViewModel
您需要像这样异步获取它:
viewModelScope.launch(Dispatchers.IO) {
allData = dao.getAll()
// It's also possible to sync other data here
}