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

如何通过OkHttp3精准获取Android应用中响应内容下载时长?

如何用OkHttp3精准获取响应内容的下载时长

我来帮你梳理下这个问题,先拆解清楚OkHttp的时间戳逻辑,再给你正确的实现方案。

首先得明确你之前的思路为什么会出现数值不稳定的情况:当chain.proceed(request)返回Response对象时,OkHttp只完成了响应头的接收,响应体的内容还在网络流里,并没有完全下载到本地。你此时取System.currentTimeMillis()和receivedResponseAtMillis()的差值,其实只是从响应头接收完成到你拿到Response对象的极短时间,根本不是响应内容的实际下载时长。

先搞懂OkHttp的几个时间戳含义

  • sentRequestAtMillis():请求头完全发送到网络的时间点
  • receivedResponseAtMillis():响应头完全从网络接收的时间点
    这两个的差值是请求往返总耗时(包含请求发送、服务器处理、响应头返回的时间),确实不包含响应体的下载时间。

正确的实现思路

要拿到纯响应内容的下载时长,你需要在响应头接收完成后,开始读取响应体的时刻到响应体完全读取完毕的时刻之间计时。因为只有读取完响应体的所有字节,才代表内容真正下载完成。

这里要注意:OkHttp的响应体流只能读取一次,所以读取后需要重新构建响应体,避免影响后续的业务逻辑。

完整代码示例

@Override
public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    Response response = chain.proceed(request);

    // 响应头接收完成的时间,也就是响应体下载开始的时间
    long contentDownloadStart = response.receivedResponseAtMillis();
    long contentDownloadEnd;

    ResponseBody originalBody = response.body();
    if (originalBody != null) {
        // 读取响应体所有内容,触发实际下载
        byte[] bodyBytes = originalBody.bytes();
        contentDownloadEnd = System.currentTimeMillis();

        // 重新构建响应体,保证后续流程能正常读取
        ResponseBody newBody = ResponseBody.create(
                originalBody.contentType(),
                originalBody.contentLength(),
                ByteString.of(bodyBytes).source()
        );
        response = response.newBuilder().body(newBody).build();
    } else {
        // 无响应体的情况,下载时长为0
        contentDownloadEnd = contentDownloadStart;
    }

    // 计算时间
    double contentDownloadTimeSecs = (contentDownloadEnd - contentDownloadStart) / 1000.0;
    double requestRoundTripTimeSecs = (contentDownloadStart - response.sentRequestAtMillis()) / 1000.0;

    // 这里可以根据需求打印或上报数据
    Log.d("BandwidthStats", String.format("URL: %s | 往返耗时: %.2fs | 内容下载耗时: %.2fs",
            request.url().toString(), requestRoundTripTimeSecs, contentDownloadTimeSecs));

    return response;
}

额外注意事项

  1. 大响应体的处理:如果你的接口返回大文件或大JSON,用bytes()或string()读取到内存可能会引发OOM。这种情况下可以用流的方式边读边计时,比如:
    InputStream inputStream = originalBody.byteStream();
    contentDownloadEnd = System.currentTimeMillis();
    // 循环读取直到流结束
    byte[] buffer = new byte[4096];
    while (inputStream.read(buffer) != -1) {
        // 可以将字节写入文件或做其他处理
    }
    contentDownloadEnd = System.currentTimeMillis();
    
  2. 网络节流测试:测试时尽量用较大的响应体(比如几MB的文件),这样内容下载时长会更明显,数值也会更稳定。小响应体的话时间差太小,容易被系统调度误差影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:36:51