【问题标题】:How to observe LiveData of a ViewModel that is not initialized during init / constructor (Room suspend function)如何观察在初始化/构造函数期间未初始化的 ViewModel 的 LiveData(房间挂起功能)
【发布时间】:2020-07-15 19:32:35
【问题描述】:

我的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 冻结)。

【问题讨论】:

  • 所以getAllItems() 返回LiveData?如果您的getAllItems() 返回LiveData,您为什么还要这样做viewModelScope.launch(IO)LiveData 返回的 Dao 方法不需要在任何调度程序上运行。
  • 伊恩,谢谢。没有什么特别的原因,我是新手,确实混淆了一些东西。它可以正常工作而无需添加另一个协程范围。请张贴作为答案。你知道LiveData重调方法在内部使用的是哪个协程范围吗?

标签: android kotlin android-room kotlin-coroutines android-viewmodel


【解决方案1】:

在使用返回 LiveData 的 Dao 方法时,您根本不需要使用 viewModelScope.launch(IO) - 返回 LiveData 根本不会真正查询数据库,直到您开始使用 observe() LiveData (此时它只使用内部的ArchTaskExecutor 从主线程运行查询)。

因此,这里根本没有理由使用lateinit var。您可以直接设置LiveData

val items = MyDatabase.getInstance(MyApplication.getContext()).itemsDao.getAllItems()

您还应该强烈考虑扩展AndroidViewModel,它会在构造函数中为您提供Application 上下文,而不是依赖MyApplication.getContext()

【讨论】:

  • 很好解释,我也会切换到AndroidViewModel,谢谢。关于ArchTaskExecutor:当我离开Fragment 并且查询仍在运行时,它会继续还是会被取消?
  • 是的,当观察者(即你的片段)停止时,待处理的工作会被清理。
【解决方案2】:

添加在视图模型初始化时创建的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 子类,它可以观察其他 LiveData 对象并对来自它们的 OnChanged 事件作出反应。 此类正确地将其活动/非活动状态传播到源 LiveData 对象。

【讨论】:

    【解决方案3】:

    如果您需要获取大量物品。我认为您可以尝试emit 像这样document 的livedata。

    视图模型:

    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)
        }
    })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-22
      • 2018-06-04
      • 1970-01-01
      • 1970-01-01
      • 2014-05-19
      • 1970-01-01
      相关资源
      最近更新 更多