Android中Retrofit循环调用时onResponse延迟触发问题排查
问题原因与解决方案
首先得明确:Retrofit的enqueue()方法是异步非阻塞的。这意味着当你调用call.enqueue()时,Retrofit会把网络请求交给后台线程去执行,当前的主线程(也就是你的循环所在线程)不会停下来等待请求完成,而是立刻继续执行下一行代码。
所以你的代码实际执行流程是这样的:
- 循环遍历
result数组,每次调用ApiHit()只是发起一个异步请求,然后立刻回到循环继续下一次迭代 - 循环结束后直接执行
FinalSort(),但这时候所有的网络请求都还在后台运行,onResponse/onFailure回调根本还没触发
解决思路:跟踪所有请求的完成状态
我们需要一个计数器来记录已经完成的请求数量,当计数器等于数组长度(所有请求都完成)时,再调用FinalSort()。同时要注意线程安全——因为Retrofit的回调默认在后台线程执行,更新计数器后要切换回主线程处理后续逻辑。
修改后的代码示例
首先在Activity里添加计数器和请求列表变量:
private int completedRequestCount = 0; // 保存所有正在执行的Call实例,方便取消未完成请求 private List<Call<Api>> ongoingCalls = new ArrayList<>();
然后修改onQueryTextSubmit方法,每次搜索前重置状态并清理旧请求:
@Override public boolean onQueryTextSubmit(String query) { // 取消之前未完成的请求,避免旧请求干扰新结果 for (Call<Api> call : ongoingCalls) { if (!call.isCanceled()) { call.cancel(); } } ongoingCalls.clear(); // 重置完成计数器 completedRequestCount = 0; result = query.split(",?\\ "); for (int i = 0; i < result.length; i++) { ApiHit(result[i]); } return true; }
接着修改ApiHit方法,管理请求实例并在回调中更新完成状态:
public void ApiHit(final String tag) { Call<Api> call = tagApi.getTasks(tag); ongoingCalls.add(call); call.enqueue(new Callback<Api>() { @Override public void onResponse(Call<Api> call, Response<Api> response) { // 处理响应数据,比如添加到你的列表中 if (response.isSuccessful() && response.body() != null) { // 示例:listTags.add(response.body()); } // 标记当前请求完成 markRequestCompleted(); } @Override public void onFailure(Call<Api> call, Throwable t) { // 处理请求失败逻辑,比如打印错误日志 t.printStackTrace(); // 无论成功失败,都标记请求完成(可根据业务需求调整) markRequestCompleted(); } }); } private void markRequestCompleted() { // 切换回主线程更新计数器并检查是否所有请求完成 runOnUiThread(new Runnable() { @Override public void run() { completedRequestCount++; if (completedRequestCount == result.length) { // 所有请求都完成了,执行最终排序 FinalSort(); // 清空请求列表 ongoingCalls.clear(); } } }); }
额外注意点
- 取消未完成请求:如果用户连续多次提交搜索,一定要取消之前未完成的请求,避免旧请求的回调干扰新结果,同时减少不必要的网络消耗和内存泄漏风险。
- 线程安全:Retrofit的回调默认在后台线程执行,所以更新计数器和触发
FinalSort()时必须切换回主线程(用runOnUiThread或Handler),否则可能导致UI异常或数据不一致。 - 错误处理灵活调整:示例中不管请求成功失败都计入完成数,你可以根据业务需求修改——比如如果某个请求失败,是否需要重试,或者是否忽略该失败请求继续执行排序。
内容的提问来源于stack exchange,提问作者Armin Orlik
相关产品推荐
相关产品推荐

