如何知道 viewModel 的作业何时完成

How to know when job from viewModel is done

我想弄清楚协同程序的作业是如何工作的。基本上,我想从 FirstFragment 启动这个协程,然后导航到 SecondFragment 并在这项工作完成时得到通知。我在 FirstFragment onViewCreated() 中调用 getData() 并导航到 SecondFragment。无论我在 SecondFragment 中写 getData().isCompleted 还是 getData().invokeOnCompletion { } 都没有任何反应。我不知道我是否遗漏了什么或没有正确开始工作或其他原因。

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 永远不会完成。此外,如果您再次调用 repo 上的 getData(),您每次都会获得一个新的 Flow 实例。

无论您在做什么,您都需要通过将其范围限定到 Activity 来确保在两个片段之间使用相同的 ViewModel 实例。 (例如使用 by activityViewModels()。)这样 viewModelScope 就不会在 Fragment 之间的过渡期间被取消。

如果您一次只需要回购中的一个项目,那么最简单的做法可能是从回购中公开一个挂起函数而不是 Flow。然后把它变成一个Deferred。也许通过将其设为 Lazy,您可以有选择地决定何时开始检索值。如果您只想在第一个片段开始时立即开始检索值,请省略 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 之前需要等待,那么您可以使 属性 惰性。

同意@Tenfour04 的回答,我想多贡献一点。 如果你真的想控制工作或 Structured Concurrency,我建议使用自定义方式来处理协同程序,而不是将你的代码与 viewModelScope.

耦合 您需要确保几件事:
1- 发生取消或异常时会发生什么
2- 你必须管理协程的生命周期 (CoroutineScope)
3- 取消范围,取决于用例,例如您现在面临的问题
4- ViewModel 的范围,例如:要么绑定到 activity(Shared ViewModel),要么绑定到特定片段。

如果你没有特别小心地处理其中任何一个,特别是前 3 个,你更有可能泄漏你的协程,你的应用程序肯定会出现不当行为。
无论何时以自定义方式启动任何协程,您都必须确保生命周期是什么,何时结束,这非常重要,它可能会导致真正的问题
幸运的是,我有这个使用 Jobs 的 CustomViewModel 示例:Structured Concurrency sample code