viewModelScope.launch协程最后一行代码未执行,求排查原因
问题描述
调用ViewModel的getNotes()方法后,日志输出顺序为:
print 1 print 2 print 5
但print 4始终未执行,请问为何控制流程无法到达getNotes()方法的最后一行?
ViewModel中的getNotes()方法
private fun getNotes() { Log.d("","print 1") viewModelScope.launch(Dispatchers.IO) { Log.d("","print 2") getNotesUserCase().collectLatest { _notesStateFlow.value = it Log.d("","print 3") } Log.d("","print 4") } }
GetNotesUserCase类
class GetNotesUserCase @Inject constructor(private val dataRepository: DataRepository) { suspend operator fun invoke(): Flow<List<Note>> { Log.d("","print 5") return dataRepository.getNotes().transform { noteEntityList -> emit(noteEntityList.map { it.toNote() }) } } }
问题原因
核心问题出在collectLatest的特性上:
collectLatest是挂起函数,它会持续监听Flow的数据流发射,只有当Flow完全结束(完成或被取消)时,才会继续执行后续代码。- 你的
dataRepository.getNotes()大概率返回的是一个永远不会主动完成的冷流(比如Room数据库的查询Flow,这类Flow会持续监听数据变化,只要ViewModelScope未被销毁,就会一直处于活跃状态)。 - 当调用
getNotesUserCase()拿到这个Flow后,collectLatest会一直挂起等待新的数据流,永远不会走到后面的print 4。
验证与解决思路
- 如果只需要获取一次数据就结束,可以把Flow转为单次获取的挂起函数,用
first()或single()替代collectLatest:// 替换原collectLatest代码块 val notes = getNotesUserCase().first() _notesStateFlow.value = notes Log.d("","print 3") Log.d("","print 4") // 此时会正常执行到这里 - 如果确实需要持续监听数据变化,那
print 4放在collectLatest之后本身就不合理——只要监听在持续,后续代码就永远没有执行机会,这种场景下应该放弃在这个位置执行后续逻辑。
内容的提问来源于stack exchange,提问作者ThunderDragon
相关产品推荐
相关产品推荐

