You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
            }
        }
    });
}

额外注意点

  1. 取消未完成请求:如果用户连续多次提交搜索,一定要取消之前未完成的请求,避免旧请求的回调干扰新结果,同时减少不必要的网络消耗和内存泄漏风险。
  2. 线程安全:Retrofit的回调默认在后台线程执行,所以更新计数器和触发FinalSort()时必须切换回主线程(用runOnUiThread或Handler),否则可能导致UI异常或数据不一致。
  3. 错误处理灵活调整:示例中不管请求成功失败都计入完成数,你可以根据业务需求修改——比如如果某个请求失败,是否需要重试,或者是否忽略该失败请求继续执行排序。

内容的提问来源于stack exchange,提问作者Armin Orlik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:52:08