如何观察在初始化/构造函数期间未初始化的 ViewModel 的 LiveData(房间暂停功能)
How to observe LiveData of a ViewModel that is not initialized during init / constructor (Room suspend function)
我的 ViewModel
通过协程中的 Room
加载数据:
// ViewModel:
lateinit var items : LiveData<List<Item>>
private set
init {
viewModelScope.launch(IO) {
items = MyDatabase.getInstance(MyApplication.getContext()).itemsDao.getAllItems()
}
}
// Fragment:
viewModel.items.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.submitList(it)
}
})
这导致
kotlin.UninitializedPropertyAccessException: lateinit property items has not been initialized
我理解这个错误 - Fragment
尝试在协程完成之前访问 items
属性,很好。
但是我该如何解决呢?如何观察一个 ViewModel
后面某个时间点实际由协程提供的数据?请注意,我希望我的查询 itemsDao.getAllItems()
保持暂停功能(没有 UI 冻结)。
添加在视图模型初始化时创建的 MediatorLiveData
。然后将您的主要 LiveData
作为来源添加到其中。
val items = MediatorLiveData<List<Item>>()
init {
viewModelScope.launch(IO) {
val dbItems = MyDatabase.getInstance(MyApplication.getContext()).itemsDao.getAllItems()
items.addSource(dbItems) { items.postValue(it) }
}
}
更多解释在 official documentation:
LiveData subclass which may observe other LiveData objects and react on OnChanged events from them.
This class correctly propagates its active/inactive states down to source LiveData objects.
如果您需要取很多物品。我想你可以尝试 emit
这样的实时数据 document.
视图模型:
fun items(): LiveData<List<Item>> = liveData(Dispatchers.IO) {
emit(MyDatabase.getInstance(MyApplication.getContext()).itemsDao.getAllItems())
}
片段:
viewModel.items.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.submitList(it)
}
})
当使用 returns 一个 LiveData
的 Dao 方法时,你根本不需要使用 viewModelScope.launch(IO)
- 返回一个 LiveData
实际上并不查询数据库一直到你开始 observe()
LiveData
(此时它使用内部仅 ArchTaskExecutor 到 运行 主线程之外的查询)。
因此根本没有理由在这里使用 lateinit var
。您可以直接设置LiveData
:
val items = MyDatabase.getInstance(MyApplication.getContext()).itemsDao.getAllItems()
您还应该强烈考虑扩展 AndroidViewModel,这会在构造函数中为您提供 Application
上下文,而不是依赖于 MyApplication.getContext()
.
我的 ViewModel
通过协程中的 Room
加载数据:
// ViewModel:
lateinit var items : LiveData<List<Item>>
private set
init {
viewModelScope.launch(IO) {
items = MyDatabase.getInstance(MyApplication.getContext()).itemsDao.getAllItems()
}
}
// Fragment:
viewModel.items.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.submitList(it)
}
})
这导致
kotlin.UninitializedPropertyAccessException: lateinit property items has not been initialized
我理解这个错误 - Fragment
尝试在协程完成之前访问 items
属性,很好。
但是我该如何解决呢?如何观察一个 ViewModel
后面某个时间点实际由协程提供的数据?请注意,我希望我的查询 itemsDao.getAllItems()
保持暂停功能(没有 UI 冻结)。
添加在视图模型初始化时创建的 MediatorLiveData
。然后将您的主要 LiveData
作为来源添加到其中。
val items = MediatorLiveData<List<Item>>()
init {
viewModelScope.launch(IO) {
val dbItems = MyDatabase.getInstance(MyApplication.getContext()).itemsDao.getAllItems()
items.addSource(dbItems) { items.postValue(it) }
}
}
更多解释在 official documentation:
LiveData subclass which may observe other LiveData objects and react on OnChanged events from them. This class correctly propagates its active/inactive states down to source LiveData objects.
如果您需要取很多物品。我想你可以尝试 emit
这样的实时数据 document.
视图模型:
fun items(): LiveData<List<Item>> = liveData(Dispatchers.IO) {
emit(MyDatabase.getInstance(MyApplication.getContext()).itemsDao.getAllItems())
}
片段:
viewModel.items.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.submitList(it)
}
})
当使用 returns 一个 LiveData
的 Dao 方法时,你根本不需要使用 viewModelScope.launch(IO)
- 返回一个 LiveData
实际上并不查询数据库一直到你开始 observe()
LiveData
(此时它使用内部仅 ArchTaskExecutor 到 运行 主线程之外的查询)。
因此根本没有理由在这里使用 lateinit var
。您可以直接设置LiveData
:
val items = MyDatabase.getInstance(MyApplication.getContext()).itemsDao.getAllItems()
您还应该强烈考虑扩展 AndroidViewModel,这会在构造函数中为您提供 Application
上下文,而不是依赖于 MyApplication.getContext()
.