android 从ViewModel观察LiveData

vqlkdk9b  于 2023-04-18  发布在  Android
关注(0)|答案(8)|浏览(172)

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

j2qf4p5b

j2qf4p5b1#

在Google开发人员Jose Alcérreca的this blog post中,建议在这种情况下使用转换(请参阅“存储库中的LiveData”段落),因为ViewModel不应该包含任何与View相关的引用(Activity,Context等),因为它很难测试。

l7mqbcuq

l7mqbcuq2#

ViewModel文档中
然而,ViewModel对象绝不能观察到生命周期感知的可观察对象(如LiveData对象)的变化。
另一种方法是让数据实现RxJava而不是LiveData,那么它就不会有生命周期感知的好处。
todo-mvvm-live-kotlin的google示例中,它在ViewModel中使用了一个没有LiveData的回调。
我猜如果你想符合生命周期件的整体思想,我们需要在Activity/Fragment中移动观察代码。否则,我们可以在ViewModel中使用回调或RxJava。
另一个折衷方案是实现MediatorLiveData(或Transformations)并在ViewModel中观察(将您的逻辑放在这里)。注意MediatorLiveData观察者不会触发(与Transformations相同),除非它在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
})

PS:我读了ViewModels and LiveData: Patterns + AntiPatterns,它建议转换。我不认为它工作,除非LiveData被观察到(这可能需要在Activity/Fragment中完成)。

rm5edbpk

rm5edbpk3#

使用Flow

文档中的指导原则被误解

然而,ViewModel对象绝不能观察到生命周期感知的可观察对象(如LiveData对象)的变化。
在这个Github问题中,他描述了应用上述规则的情况是,观察到的生命周期感知的可观察对象由另一个生命周期范围托管。在ViewModel中观察到LiveData包含观察到的LiveData没有问题。

使用Flow

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

    init {
        viewModelScope.launch {
            myLiveData.asFlow().collect {
                // Do Something
            }
        }
    }
}

使用StateFlow

class MyViewModel : ViewModel() {
    private val myFlow = MutableStateFlow(1)
    private val myLiveData = myFlow.asLiveData(viewModelScope.coroutineContext)
}

需要"androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"依赖项

PS

asFlow创建了一个流,使LiveData在启动collect时激活。我认为使用MediatorLiveDataTransformations并附加一个虚拟观察者的解决方案与使用Flow没有区别,除了从LiveData发出的值总是在ViewModel示例中观察到。

6ie5vjzr

6ie5vjzr4#

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

50pmv0ei

50pmv0ei5#

Use Kotlin coroutines with Architecture components.
您可以使用liveData构建器函数调用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或更高版本。
还有一个article

更新:也可以在Daointerface中修改LiveData<YourData>,需要在函数中添加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
}
inkz8wg9

inkz8wg96#

自从最初的帖子以来已经有一段时间了,但我最近偶然发现了同样的问题(也是Firebase),我能够用Transformations解决它。
我有一个存储库类,它保存了用Firebase的ValueEventListener收集的liveData对象。
现在,在ViewModel中,不是有一个从存储库返回LiveData值的函数,然后通过观察者将其传递给片段,就像这样:

fun getMyPayments(): LiveData<HashMap<String, Int>> {
    return repository.provideMyPayments()
}

我在www.example.com中使用了一个瓦尔Transformations.map它表示LiveData的最终结果,在被ViewModel中的另一个函数处理之后:

val myRoomPaymentsList : LiveData<HashMap<String, HashMap<String, Payment>>> = Transformations.map(repository.provideMyPayments()) {data ->
        getRoomPaymentsList(data)
    }

注意,第一个参数是你观察到的数据源,第二个参数是你想要得到的结果。这个瓦尔是一个LiveData val,它保存存储库中的最新值,并根据需要在Fragment中提供它,将所有处理都保留在ViewModel中,只有UI函数在Fragent本身中。
然后,在我的片段中,我在这个瓦尔上放置了一个观察者:

viewModel.myRoomPaymentsList.observe(viewLifecycleOwner, {
    roomPayments = it
    graphFilterPeriod()
})
7dl7o3gd

7dl7o3gd7#

例如,如果你需要获取一个ID(作为LiveData)并使用它来调用LiveData。将ID存储在selectedID中,Transformations将观察该字段并在它发生变化时调用getAllByID(selectedID)(也是LiveData)。

var selectedID = MutableLiveData<Int>()

val objects = Transformations.switchMap(selectedID) { getAllByID(it) }
qvk1mo1f

qvk1mo1f8#

我知道这个主题已经有了惊人的答案,但我也想加上我自己的:
如果你想坚持使用LiveData,你可以一直使用Transformations.map,这样你就不必在ViewModel中使用observe,而只需要在Fragment/Activity中使用。
否则,你可以使用SharedFlow,一个单一的可观察事件。更多信息,我在这里写了一篇文章:https://coroutinedispatcher.com/posts/decorate_stateful_mvvm/
您不必在ViewModel中传递viewLifecycleOwner,因为在ViewModel中调用observe没有意义,因为View毕竟只需要最新的结果。

相关问题