android - 如何知道 viewModel 的工作何时完成
问题描述
我试图弄清楚协同程序的工作是如何工作的。基本上,我想启动这个协程FirstFragment
,然后导航到SecondFragment
并在完成这项工作时得到通知。我打电话getData()
并FirstFragment
onViewCreated()
导航到SecondFragment
。无论我写getData().isCompleted
还是什么getData().invokeOnCompletion { }
都SecondFragment
没有发生。我不知道我是否遗漏了什么或没有正确开始工作或其他什么。
private val _data = MutableStateFlow<GetResource<String>?>(null)
val data: StateFlow<GetResource<String>?> = _data
fun getData() = viewModelScope.launch {
repository.getData().collect {
_data.value = it
}
}
解决方案
来自数据库的流永远不会完成,因为它应该无限期地监视数据库的更改。它仅在协程被取消时停止。因此,收集此类 Flow 的 Job 将永远不会完成。此外,如果您getData()
再次调用 repo,您每次都会获得一个新的 Flow 实例。
无论您在做什么,您都需要确保在两个片段之间使用相同的 ViewModel 实例,方法是将其范围限定为 Activity。(by activityViewModels()
例如使用。)这是为了viewModelScope
在片段之间的过渡期间不会被取消。
如果您需要的只是一次来自 repo 的单个项目,那么最简单的方法可能是从 repo 中公开一个挂起函数而不是 Flow。然后把它变成一个延迟。也许通过将其设为 a Lazy
,您可以有选择地决定何时开始检索该值。lazy
如果您只想在第一个 Fragment 开始时立即开始检索值,请省略。
// 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()
上面。shareIn
在您的 ViewModel 中,执行此操作比必须使用支持属性并手动收集源代码的代码更容易。
val data: SharedFlow<GetResource<String>> = repository.getData()
.shareIn(viewModelScope, replay = 1, SharingStarted.Eagerly)
如果您需要在开始收集到 SharedFlow 之前等待,那么您可以使属性变得惰性。
推荐阅读
- python - 使用 Beautiful Soup find_all() 仅在 align=right 时查找
- java - 无法在 JMC for Java 应用程序和 Eclipse IDE 中打开飞行记录器
- r - 使用 URL 连接到 HDFS 文件系统时,如果 R 中的第一个路径出现异常,如何转到下一个路径
- java - 如何更改警报对话框中按钮的颜色?
- c# - 使用 powershell 加载 C# DLL,[System.Type]::GetType 返回 null
- node.js - 在节点中解析 ByteBuffer?
- node.js - 谷歌应用引擎作为图像服务器
- html - 如何让我的文本成为带有填充和省略号的两行?
- ios - tableView 不显示我的数据
- php - Wooleet 区块链 API 错误(使用 PHP CURL)