从ViewModel观察LiveData

Vuk*_*bic 55 android android-livedata android-viewmodel android-architecture-components

我有一个单独的类来处理数据提取(特别是Firebase),我通常从它返回LiveData对象并异步更新它们.现在我想将返回的数据存储在ViewModel中,但问题是为了获得所述值,我需要观察从我的数据获取类返回的LiveData对象.observe方法需要一个LifecycleOwner对象作为第一个参数,但我显然没有在我的ViewModel内部,我知道我不应该保持对ViewModel内部的Activity/Fragment的引用.我该怎么办?

gug*_*upf 23

在Google开发人员JoseAlcérreca的博客文章中,建议在这种情况下使用转换(请参阅"存储库中的LiveData"段落).

  • 我不知道为什么这是推荐答案,它与问题无关。两年后,我们仍然不知道如何在视图模型中观察存储库数据的变化。 (21认同)
  • 转换本身不起作用,因为您在转换中编写的任何代码仅在某个实体观察到转换时附加到运行. (11认同)
  • 这没有回答问题?如果您需要视图模型中的特定值来执行其他操作然后将其提供给片段怎么办? (2认同)

小智 22

我认为您可以使用不需要生命周期所有者接口的observeForever,您可以从视图模型中观察结果

  • 抱歉,但是“无法在后台线程上调用observeForever” (5认同)
  • 这对我来说似乎是正确的答案,特别是在有关 ViewModel.onCleared() 的文档中说:“当 ViewModel 观察到某些数据并且您需要清除此订阅以防止此 ViewModel 泄漏时,它很有用。” (2认同)
  • 这看起来很合理。尽管必须将观察者保存在 viewModel 字段中并在“onCleared”取消订阅。至于后台线程——从主线程观察,就是这样。 (2认同)

Des*_*Lua 19

ViewModel文档中

但是,ViewModel对象绝不能观察到生命周期感知的可观察对象(例如LiveData对象)的更改.

另一种方法是数据实现RxJava而不是LiveData,然后它不具备生命周期感知的好处.

todo-mvvm-live-kotlin的 google示例中,它使用ViewModel中没有LiveData的回调.

我猜你是否想要遵守生命周期的整个想法,我们需要在Activity/Fragment中移动观察代码.否则,我们可以在ViewModel中使用回调或RxJava.

另一个折衷方案是在ViewModel中实现MediatorLiveData(或Transformations)并观察(将您的逻辑放在这里).注意MediatorLiveData观察者不会触发(与转换相同),除非在Activity/Fragment中观察到它.我们所做的是在Activity/Fragment中进行空白观察,其中实际工作实际上是在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
})
Run Code Online (Sandbox Code Playgroud)

PS:我读过ViewModels和LiveData:Patterns + AntiPatterns,它们提出了转换.除非观察到LiveData(可能需要在Activity/Fragment中完成),否则我认为它不起作用.

  • 有什么解决方案可以摆脱这些空白观察? (3认同)
  • 在这方面有什么变化吗?或者RX,回调或空白观察只是解决方案? (2认同)
  • 也许使用 Flow (`mLiveData.asFlow()`) 或 `observeForever`。 (2认同)

Psi*_*jic 7

将 Kotlin 协程与架构组件一起使用。

您可以使用liveDatabuilder 函数调用suspend函数,将结果作为LiveData对象提供。

val user: LiveData<User> = liveData {
    val data = database.loadUser() // loadUser is a suspend function.
    emit(data)
}
Run Code Online (Sandbox Code Playgroud)

您还可以从块中发出多个值。每次emit()调用都会暂停块的执行,直到LiveData在主线程上设置该值。

val user: LiveData<Result> = liveData {
    emit(Result.loading())
    try {
        emit(Result.success(fetchUser()))
    } catch(ioException: Exception) {
        emit(Result.error(ioException))
    }
}
Run Code Online (Sandbox Code Playgroud)

在您的 gradle 配置中,使用androidx.lifecycle:lifecycle-livedata-ktx:2.2.0或更高版本。

还有一篇关于它的文章

更新:也可以LiveData<YourData>Dao interface. 您需要suspend在函数中添加关键字:

@Query("SELECT * FROM the_table")
suspend fun getAll(): List<YourData>
Run Code Online (Sandbox Code Playgroud)

并且ViewModel您需要像这样异步获取它:

viewModelScope.launch(Dispatchers.IO) {
    allData = dao.getAll()
    // It's also possible to sync other data here
}
Run Code Online (Sandbox Code Playgroud)


MJ *_*dio 7

使用流程

文档中的指南被误解了

然而,ViewModel 对象绝不能观察生命周期感知可观察对象的变化,例如 LiveData 对象。

在这个Github issue 中,他描述了应用上述规则的情况是观察到的生命周期感知 observables 由另一个生命周期范围托管。LiveDataViewModel包含观察中观察没有问题LiveData

使用流程

class MyViewModel : ViewModel() {
    private val myLiveData = MutableLiveData(1)

    init {
        viewModelScope.launch {
            myLiveData.asFlow().collect {
                // Do Something
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用状态流

class MyViewModel : ViewModel() {
    private val myFlow = MutableStateFlow(1)
    private val myLiveData = myFlow.asLiveData(viewModelScope.coroutineContext)
}
Run Code Online (Sandbox Code Playgroud)

聚苯乙烯

asFlow使一个流,使LiveData激活的开始collect。我认为使用MediatorLiveDataorTransformations和附加虚拟观察者的解决方案在实例中始终观察到使用Flow除了发射值之外的差异。LiveDataViewModel

  • 这很好,但是“asFlow”从哪里来?没有依赖名称 (3认同)