如何观察在初始化/构造函数期间未初始化的 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().