Android协程并发执行问题:本地数据先返回,网络请求并行
Android Flow 并行逻辑实现问题
核心需求
- 先通过Flow发射本地缓存数据,让ViewModel立即接收
- 并行发起网络请求,不阻塞本地数据的发射流程
- 网络请求成功后,更新本地缓存并再次通过Flow发射最新网络数据
当前代码问题
当前代码会等待网络请求完成后才继续执行后续逻辑,导致ViewModel无法及时收到本地数据,日志顺序不符合预期。
原代码
private val _myFlow = MutableStateFlow<Result<Flow<ResponseDataClass>>>(Result.Success(emptyFlow())) suspend fun getSomethingFromPreferences(): Result<Flow<ResponseDataClass>> { Log.d("Coroutines", "getSomethingFromPreferences") prefs.myDataInPreferences?.takeIf { it.requiredList.isNotEmpty() }?.let { Log.d("Coroutines", "prefs.myDataInPreferences is not empty") _myFlow.emit(Result.Success(flowOf(it))) } coroutineScope { async(Dispatchers.IO) { getDataFromNetwork() } } Log.d("Coroutines", "finished") return _myFlow.value } private suspend fun getDataFromNetwork() { Log.d("Coroutines", "getDataFromNetwork") // val result = Call network API if (result is Result.Success) { Log.d("Coroutines", "result is Success") prefs.myDataInPreferences = result.data _myFlow.value = Result.Success(flowOf(result.data)) } else { Log.d("Coroutines", "result is Error") _myFlow.value = Result.Error(Error(result.error.message)) } }
日志对比
期望日志顺序:
2023-07-04 13:07:36.339 D/Application: getSomethingFromPreferences 2023-07-04 13:07:36.339 D/Application: prefs.myDataInPreferences is not empty 2023-07-04 13:07:36.339 D/Application: getDataFromNetwork 2023-07-04 13:07:36.339 D/Application: finished 2023-07-04 13:07:36.339 D/Application: Received data in viewModel 2023-07-04 13:07:36.339 D/Application: result is Success
实际日志顺序:
2023-07-04 13:07:36.339 D/Application: getSomethingFromPreferences 2023-07-04 13:07:36.339 D/Application: prefs.myDataInPreferences is not empty 2023-07-04 13:07:36.339 D/Application: getDataFromNetwork 2023-07-04 13:07:36.339 D/Application: result is Success 2023-07-04 13:07:36.339 D/Application: finished 2023-07-04 13:07:36.339 D/Application: Received data in viewModel
问题分析
coroutineScope的特性是等待内部所有子协程执行完毕后,才会继续执行后续代码——即使使用async,当前函数仍会阻塞等待网络请求完成,才会打印finished并返回结果,导致ViewModel无法及时收到本地数据。
同时,原代码中MutableStateFlow嵌套Flow的设计冗余,StateFlow本身支持多次发射数据,无需额外嵌套包装。
解决方案
优化后代码
// 简化StateFlow定义,直接发射数据结果,无需嵌套Flow private val _myFlow = MutableStateFlow<Result<ResponseDataClass>>(Result.Success(emptyResponse())) // 替换为你的空ResponseDataClass实例 val myFlow: StateFlow<Result<ResponseDataClass>> = _myFlow.asStateFlow() suspend fun getSomethingFromPreferences() { Log.d("Coroutines", "getSomethingFromPreferences") prefs.myDataInPreferences?.takeIf { it.requiredList.isNotEmpty() }?.let { Log.d("Coroutines", "prefs.myDataInPreferences is not empty") _myFlow.emit(Result.Success(it)) // 直接发射本地数据 } // 用ViewModelScope启动独立协程执行网络请求,不阻塞当前函数 viewModelScope.launch(Dispatchers.IO) { getDataFromNetwork() } Log.d("Coroutines", "finished") } private suspend fun getDataFromNetwork() { Log.d("Coroutines", "getDataFromNetwork") val result = // 调用网络API if (result is Result.Success) { Log.d("Coroutines", "result is Success") prefs.myDataInPreferences = result.data _myFlow.value = Result.Success(result.data) // 发射最新网络数据 } else { Log.d("Coroutines", "result is Error") _myFlow.value = Result.Error(Error(result.error.message)) } }
优化说明
- 简化Flow结构:去掉嵌套的
Flow,直接用MutableStateFlow<Result<ResponseDataClass>>发射数据,ViewModel只需收集myFlow即可接收所有数据更新。 - 并行网络请求:使用
viewModelScope.launch启动网络请求协程,该协程与当前函数并行执行,不会阻塞本地数据的发射和函数返回,确保ViewModel能立即收到本地缓存。 - 符合预期的日志顺序:优化后函数会先打印
finished,ViewModel收到本地数据后打印Received data in viewModel,最后网络请求完成打印result is Success,完全匹配期望顺序。
内容的提问来源于stack exchange,提问作者Ali Kazi
相关产品推荐
相关产品推荐

