Kotlin协程:如何等待多API请求完成后更新UI?
问题分析
你的核心问题在于API请求的异步逻辑和协程的等待逻辑不匹配:
getScore方法使用Retrofit的enqueue发起请求,这是基于回调的异步操作,协程中调用getScore(type)只是触发了请求,并不会等待API响应返回就直接执行后续代码。- 因此
job.join()会在所有请求发起后立刻完成,而非等待所有请求的响应结果回来,导致加载器提前关闭。即使改用async,如果内部还是用回调式的API调用,也会出现同样的问题。
解决方案
步骤1:将Retrofit回调转为协程挂起函数
Retrofit 2.6.0及以上版本原生支持协程,我们可以用await()替代enqueue,把API调用改成挂起函数,让协程能够等待请求完成:
private suspend fun getScore(type: String): Score? { return try { val response = MyApi.getScores(type).await() if (response.isSuccessful) { response.body() } else { // 处理请求失败(比如状态码非2xx) null } } catch (e: Exception) { // 处理网络异常、超时等情况 null } }
如果你的Retrofit版本低于2.6.0,需要添加kotlinx-coroutines-retrofit依赖才能使用await()扩展函数
步骤2:用协程并行执行并等待所有请求完成
改用lifecycleScope(或viewModelScope,根据组件生命周期选择)管理协程,避免内存泄漏,同时用async+awaitAll实现并行请求并等待全部完成:
private fun getScoreForType() { lifecycleScope.launch { showLoader() // 显示加载器 // 并行发起所有API请求,每个请求返回一个Deferred对象 val deferredTasks = types.map { type -> async(Dispatchers.IO) { getScore(type) } } // 等待所有请求完成,获取全部结果 val scoreResults = deferredTasks.awaitAll() // 处理所有有效结果,更新UI scoreResults.forEachNotNull { score -> setScore(score) } // 所有请求完成后关闭加载器 dismissLoader() } }
关键说明
- 用
lifecycleScope/viewModelScope替代手动创建的CoroutineScope:这类生命周期绑定的协程会在组件销毁时自动取消,避免内存泄漏。 async用于创建并行的异步任务,awaitAll()会阻塞当前协程,直到所有Deferred任务都完成,确保所有API响应都返回后再执行UI更新和关闭加载器的逻辑。- 挂起函数
getScore让协程能够等待API请求完成,而非通过回调异步执行,保证了协程等待逻辑的准确性。
内容的提问来源于stack exchange,提问作者MADi
相关产品推荐
相关产品推荐

