You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获知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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 08:27:02