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

如何从Retrofit API请求中获取中间响应 解决大响应OOM及无分段回调问题

问题根因定位
  • HttpLoggingInterceptor日志级别设置为BODY会提前读取完整响应体存入内存,直接导致@Streaming注解失效,分片读取的能力被拦截器提前消耗
  • 即使@Streaming生效,如果消费响应时直接调用ResponseBody.string()/bytes()方法,依然会一次性加载全量响应到内存触发OOM
  • 当前接口返回类型为ResponseBody未走Gson全量序列化逻辑,这部分配置是正确的
具体修复步骤

1. 调整日志拦截器配置

BODY级别的日志会强制读取全量响应,是导致@Streaming失效的核心原因,可以根据环境调整日志级别,或者精准判断请求路径,仅对大响应接口关闭BODY级日志:

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
// 测试环境用HEADERS级别,生产环境关闭日志;也可以在拦截器内判断路径为SEARCH_DOCUMENTS时跳过响应日志打印
interceptor.setLevel(BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.HEADERS : HttpLoggingInterceptor.Level.NONE);

2. 调整响应消费逻辑,逐段读取分片

拿到ResponseBody后禁止直接调用string()/bytes()方法,通过流逐段读取,搭配Gson流式解析器JsonReader逐个解析数据,避免全量加载到内存,同时可以实现进度回调:

searchDocuments(documentRequest)
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.io()) // 流式解析全程在子线程执行,不要提前切主线程
    .subscribe(responseBody -> {
        JsonReader jsonReader = new JsonReader(new InputStreamReader(responseBody.byteStream()));
        jsonReader.beginArray(); // 按实际响应结构调整,假设外层为JSON数组
        Gson gson = new Gson();
        int parsedCount = 0;
        while (jsonReader.hasNext()) {
            // 逐个解析数组内的单条数据
            DocumentItem item = gson.fromJson(jsonReader, DocumentItem.class);
            // 此处可做增量存储到数据库、回调进度等操作
            parsedCount++;
            // 每解析100条更新一次进度到UI
            if (parsedCount % 100 == 0) {
                int finalParsedCount = parsedCount;
                runOnUiThread(() -> updateProgressBar(finalParsedCount));
            }
        }
        jsonReader.endArray();
        jsonReader.close();
        responseBody.close();
        // 全部解析完成回调
        runOnUiThread(() -> loadFinish());
    }, throwable -> {
        // 异常处理逻辑
        runOnUiThread(() -> showLoadError(throwable.getMessage()));
    });

3. 可选优化配置

可以调整OkHttpClient的缓冲区大小适配流式读取场景,提升读取效率:

OkHttpClient client = new OkHttpClient.Builder()
    // 原有配置保持不变
    .readBufferSize(16 * 1024) // 设置16KB读缓冲区
    .writeBufferSize(16 * 1024) // 设置16KB写缓冲区
    .build();
注意事项
  • 流式读取、解析的全流程必须在子线程执行,避免阻塞主线程触发ANR
  • 操作完成后必须主动关闭JsonReader和ResponseBody,避免资源泄漏
  • 解析到的单条数据建议直接存入本地数据库,不要全部缓存在内存中,解析完成后再从数据库读取展示

内容的提问来源于stack exchange,提问作者Rohan Patel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 08:51:03