Android ViewModel协程获取的steps列表第二个元素无法在Fragment显示
问题原因
核心问题是异步协程的执行时序和数据缺少可观察能力两个原因导致:
getSteps()内部通过viewModelScope.launch启动了异步协程执行数据库查询操作,你在Fragment中调用完该方法后立刻判断viewModel.steps是否为空时,协程还未完成数据库查询,steps仍然是初始的空列表,所以整个UI赋值的if分支根本不会执行。你在协程中打印的日志能看到size不为空,是因为日志打印在协程执行完成之后,此时Fragment中的判断逻辑早就执行完毕了。- 你的
steps是普通的var变量,就算后续协程执行完成更新了变量值,也没有通知机制触发UI重新刷新,所以UI永远不会拿到最新的steps数据更新。
修复方案
1. ViewModel层改造,替换为可观察数据类型
推荐用StateFlow存储steps数据,代码示例:
val workout = state.get<Workout>("workout") // 改为可观察的StateFlow private val _steps = MutableStateFlow<List<Step>>(emptyList()) val steps: StateFlow<List<Step>> = _steps.asStateFlow() fun getSteps() = viewModelScope.launch { val stepList = workout?.let { stepDao.getWorkoutSteps(it.workoutId) } ?: emptyList() _steps.value = stepList Log.e("KEK", stepList.size.toString()) }
2. Fragment层改为观察数据变化后更新UI
不要同步判断steps是否为空,改为监听steps数据变化后再执行UI赋值,同时增加长度判断避免数组越界:
viewModel.getSteps() lifecycleScope.launchWhenStarted { viewModel.steps.collect { steps -> // 注意要判断长度>=2再取索引1,避免只有1个元素时出现下标越界崩溃 if (steps.size >= 2) { binding.apply { currentStepName.text = steps[0].name currentStepDuration.text = steps[0].length.toString() nextStep.text = "${steps[1].name} : ${steps[1].length}" } } } }
内容的提问来源于stack exchange,提问作者salah1337
相关产品推荐
相关产品推荐

