Android Kotlin协程请求API返回结果顺序错乱问题求助
问题分析
你的代码存在两个核心问题导致结果乱序/重复:
- 共享成员变量
id在多协程中被并发修改,存在竞态条件——多个协程可能同时读取到相同的id值,导致请求重复; - 每次调用
fetchApiResponse都会启动独立协程,API响应时间不确定,先完成的请求会先回调View,导致结果顺序随机。
解决方案
方案一:串行执行请求(严格按顺序发起+返回)
适合需要严格按顺序发起请求的场景(比如API有调用频率限制),前一个请求完成后再发起下一个,保证结果顺序完全符合预期。
修改代码如下:
object RestModel : MVPContract.Model { private val apiClient = APIClient.getInstance() private var job: Job? = null // 新增方法:一次性处理1到10的串行请求 fun fetchSequentialResponses(presenter: MVPContract.Presenter) { job = CoroutineScope(Dispatchers.IO).launch { for (id in 1..10) { try { val response = apiClient.getCharacterById(id) withContext(Dispatchers.Main) { val apiResponse = presenter.processRawJson(response.body()!!) presenter.passResponseToView(apiResponse) } // 可选:如果API有频率限制,添加间隔 // delay(300) } catch (ex: Exception) { withContext(Dispatchers.Main) { presenter.passErrorResponseMessageToView(ex.message.toString()) } } } } } fun cancelJob() { job?.cancel() } // 保留原接口的话,改为接收id参数,避免共享变量 override fun fetchApiResponse(id: Int, presenter: MVPContract.Presenter) { job = CoroutineScope(Dispatchers.IO).launch { try { val response = apiClient.getCharacterById(id) withContext(Dispatchers.Main) { val apiResponse = presenter.processRawJson(response.body()!!) presenter.passResponseToView(apiResponse) } } catch (ex: Exception) { withContext(Dispatchers.Main) { presenter.passErrorResponseMessageToView(ex.message.toString()) } } } } }
使用示例(Presenter中):
// 方式1:调用新增的串行方法 RestModel.fetchSequentialResponses(this) // 方式2:外部循环传入id(需确保串行执行) CoroutineScope(Dispatchers.Main).launch { for (id in 1..10) { // 用async/await保证串行 async(Dispatchers.IO) { RestModel.fetchApiResponse(id, this@MyPresenter) }.await() } }
方案二:并行请求+按顺序整理结果
适合API支持并发请求的场景,同时发起所有请求,等全部完成后按id排序再返回,效率更高。
修改代码如下:
object RestModel : MVPContract.Model { private val apiClient = APIClient.getInstance() private var job: Job? = null fun fetchParallelOrderedResponses(presenter: MVPContract.Presenter) { job = CoroutineScope(Dispatchers.IO).launch { // 批量创建并行请求的Deferred任务 val deferredTasks = (1..10).map { id -> async { // 保存id和对应响应,用于后续排序 id to apiClient.getCharacterById(id) } } // 等待所有请求完成 val allResults = deferredTasks.awaitAll() // 按id排序结果 val sortedResults = allResults.sortedBy { it.first } // 切换到主线程,按顺序回调View withContext(Dispatchers.Main) { sortedResults.forEach { (id, response) -> try { val apiResponse = presenter.processRawJson(response.body()!!) presenter.passResponseToView(apiResponse) } catch (ex: Exception) { presenter.passErrorResponseMessageToView(ex.message.toString()) } } } } } fun cancelJob() { job?.cancel() } }
使用示例:
RestModel.fetchParallelOrderedResponses(this)
关键注意事项
- 彻底抛弃共享可变的
id成员变量,改用参数传递id,避免线程安全问题; - 协程作用域建议使用Android生命周期绑定的
viewModelScope/lifecycleScope,而非直接创建CoroutineScope(Dispatchers.IO),防止内存泄漏; - 每个请求单独处理异常,避免单个请求失败导致整个流程中断。
内容的提问来源于stack exchange,提问作者user21597761
相关产品推荐
相关产品推荐

