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

如何使用CompletableFuture等待所有OKHttp异步网络请求全部完成

问题根因

你当前的实现逻辑存在两个核心问题:

  • CompletableFuture.runAsync 会在传入的Runnable执行完成后就标记Future为完成状态,而你写的getResponse方法内部调用OkHttp的enqueue是异步非阻塞方法,getResponse把请求提交给OkHttp之后就直接返回了,不会等待网络请求的回调触发,所以你加到列表里的所有Future会在所有请求提交完成后就全部进入完成状态,根本不会等响应返回。
  • 原逻辑没有做响应结果透传,后续也没法收集所有响应列表。

代码修改实现

第一步:改造getResponse方法,改为返回CompletableFuture

把getResponse的返回值从void改成CompletableFuture<String>,在OkHttp的回调里手动完成Future:

private CompletableFuture<String> getResponse(final Context context, final String id) {
    // 先创建一个未完成的CompletableFuture
    CompletableFuture<String> future = new CompletableFuture<>();
    Log.d("api_log", "id is: " + id);
    final String url = context.getString(R.string.myurl) + "/" + id;
    OkHttpClient client = new OkHttpClient().newBuilder()
            .build();

    Request request = new Request.Builder()
            .url(url)
            .method("GET", null)
            .build();

    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            e.printStackTrace();
            // 请求失败标记Future完成,可根据需求选择传异常或者null
            future.completeExceptionally(e);
        }

        @Override
        public void onResponse(Call call, final Response response) throws IOException {
            try {
                if (!response.isSuccessful()) {
                    // 响应异常标记完成
                    future.complete(null);
                    return;
                }
                final String strResponse = response.body().string();
                Log.d("api_log", "response: " + strResponse);
                // 把响应结果设置到Future里,标记为完成
                future.complete(strResponse);
            } catch (Exception e) {
                future.completeExceptionally(e);
            } finally {
                response.close();
            }
        }
    });
    // 直接返回未完成的Future,等回调触发时才会进入完成状态
    return future;
}

第二步:调整调用逻辑,收集所有Future

不需要额外用runAsync包裹,直接把getResponse返回的Future加到列表里即可,最后通过allOf等待所有Future完成后,还可以统一收集所有响应结果:

Log.d("api_log", "Started doing things");

List<CompletableFuture<String>> futures = new ArrayList<>();
for (int i = 0; i < mylist.size(); i++) {
    try {
        // 直接添加getResponse返回的Future即可,不需要runAsync包裹
        futures.add(getResponse(context, mylist.get(i).id));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
        .thenRunAsync(() -> {
            Log.d("api_log", "Ended doing things");
            // 这里可以统一收集所有响应结果
            List<String> allResponses = new ArrayList<>();
            for (CompletableFuture<String> future : futures) {
                try {
                    // 此时future已经全部完成,get不会阻塞
                    String resp = future.get();
                    if (resp != null) {
                        allResponses.add(resp);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            // 在这里处理所有响应的业务逻辑即可
        });

可选优化点

  • 不要每次请求都新建OkHttpClient实例,全局复用一个单例即可,避免重复创建连接池导致的性能损耗。
  • 如果需要处理请求失败的场景,可以在completeExceptionally之后在allOf的回调里统一处理异常。

内容的提问来源于stack exchange,提问作者Erez git

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 10:09:02