如何获知ViewModel中启动的协程Job是否已执行完成
问题根因
你当前代码的核心问题是 每次调用getData()方法都会通过viewModelScope.launch创建全新的协程Job:你在FirstFragment调用getData()时启动的是JobA,跳转到SecondFragment后再次调用getData()拿到的是全新的JobB,和之前运行的JobA完全无关,自然无法获取到正确的任务状态、也收不到完成回调。
修复方案
第一步:ViewModel中缓存已启动的Job
先在ViewModel中声明变量存储已经启动的协程Job,避免每次调用都创建新实例:
// 缓存已启动的getData任务Job private var getDataJob: Job? = null private val _data = MutableStateFlow<GetResource<String>?>(null) val data: StateFlow<GetResource<String>?> = _data fun getData(): Job? { // 任务已活跃时直接返回已有Job,避免重复启动 if (getDataJob?.isActive == true) return getDataJob return viewModelScope.launch { repository.getData().collect { _data.value = it // 若你的数据流是一次性请求,拿到最终结果后协程会自动结束 // 若为持续发射的热流,可在符合业务完成条件时调用cancel()主动结束任务 } }.also { getDataJob = it } }
第二步:SecondFragment选择合适的监听方式
有两种可选实现,优先推荐方案2,更符合Flow的设计规范:
- 方案1:监听Job状态
首先确保FirstFragment和SecondFragment使用同一个ViewModel实例(建议用导航图范围/Activity范围的ViewModel,不要用各自Fragment范围的实例),之后直接获取缓存的Job操作即可:// 设置完成回调 sharedViewModel.getData()?.invokeOnCompletion { throwable -> // throwable不为空代表协程因异常终止,为空代表正常执行完成 } // 判断任务是否完成 val taskCompleted = sharedViewModel.getData()?.isCompleted ?: false - 方案2:直接监听StateFlow(推荐)
你已经将请求结果存入data这个StateFlow,不需要额外绑定Job的生命周期,直接监听Flow的变化即可收到任务完成通知:lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { sharedViewModel.data.collect { resource -> resource ?: return@collect // 此处收到数据更新即代表任务执行到对应阶段,可根据resource的状态判断是否完成 } } }
注意事项
如果repository.getData()返回的是会持续发射数据的热流,那么协程会一直处于活跃状态,isCompleted永远返回false,需要你根据业务逻辑判断任务完成的条件(比如收到第一个非Loading状态的结果就视为完成),符合条件时主动调用getDataJob?.cancel()结束协程。
内容的提问来源于stack exchange,提问作者TheRock
相关产品推荐
相关产品推荐

