来自数据库的流永远不会完成,因为它应该无限期地监视数据库的更改。它仅在协程被取消时停止。因此,收集此类 Flow 的 Job 将永远不会完成。此外,如果您再次在 repo 上调用 getData(),您每次都会获得一个新的 Flow 实例。
无论您在做什么,您都需要确保您在两个片段之间使用相同的 ViewModel 实例,方法是将其范围限定为 Activity。 (例如使用by activityViewModels()。)这样viewModelScope 在片段之间的转换过程中不会被取消。
如果您只需要一次来自 repo 的单个项目,那么最简单的方法可能是从 repo 中公开一个挂起函数而不是 Flow。然后把它变成一个延迟。也许通过将其设为Lazy,您可以有选择地决定何时开始检索该值。如果您只想在第一个 Fragment 开始时立即开始检索值,请省略 lazy。
// In the shared view model:
val data: Deferred<GetResource<String>> by lazy {
viewModelScope.async {
repository.getData() // suspend function returning GetResource<String>
}
}
fun startDataRetrieval() { data } // access the lazy property to start its coroutine
// In second fragment:
lifecycleScope.launch {
val value = mySharedViewModel.data.await()
// do something with value
}
但如果您必须拥有 Flow,因为您将其用于其他目的:
如果您只想要 Flow 中的第一个可用值,请让第二个 Fragment 监控您的 data StateFlow 的第一个有效值。
lifecycleScope.launch {
val value = mySharedViewModel.data.filterNotNull().first()
// do something with first arrived value
}
而且您可以使用 SharedFlow,这样您就不必使数据类型可以为空。如果你这样做,你可以省略上面的filterNotNull()。在您的 ViewModel 中,使用 shareIn 执行此操作比您必须使用支持属性并手动收集源代码的代码更容易。
val data: SharedFlow<GetResource<String>> = repository.getData()
.shareIn(viewModelScope, replay = 1, SharingStarted.Eagerly)
如果您需要在开始收集到 SharedFlow 之前等待,那么您可以使属性变得惰性。