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

如何测量Retrofit端到端网络通信时长?

实现Retrofit全请求端到端时长统计

当然可以!因为Retrofit底层是基于OkHttp实现的,所以最干净、无侵入的方案就是用OkHttp的**拦截器(Interceptor)**来统计所有请求的端到端时长——它能在请求发起前和响应(或错误)返回后插入自定义逻辑,正好满足全链路时长统计的需求。

核心思路:利用OkHttp应用拦截器

OkHttp的拦截器分为应用拦截器和网络拦截器,如果要统计完整的端到端时长(从调用Retrofit接口到拿到响应/抛出异常的总时间),推荐用应用拦截器——它只会在每次完整请求触发一次,不会被重定向、重试等中间操作打断,能准确反映用户感知的请求耗时。

具体代码实现

1. 自定义时长统计拦截器

创建一个拦截器,在请求发起前记录时间戳,请求完成(成功/失败)后计算耗时:

class TimingInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        // 记录请求启动的纳秒时间戳
        val startTime = System.nanoTime()
        val request = chain.request()
        val url = request.url.toString()

        return try {
            // 执行请求
            val response = chain.proceed(request)
            // 计算耗时(转成毫秒,更易读)
            val elapsedMs = (System.nanoTime() - startTime) / 1_000_000
            // 这里可以根据需求处理时长数据:打印日志、存入统计库、上报监控平台等
            Log.d("NetworkStats", "✅ Request to $url took $elapsedMs ms")
            response
        } catch (e: Exception) {
            // 请求失败也要统计耗时
            val elapsedMs = (System.nanoTime() - startTime) / 1_000_000
            Log.e("NetworkStats", "❌ Request to $url failed after $elapsedMs ms", e)
            // 不要吞掉异常,继续抛出让上层处理
            throw e
        }
    }
}

2. 将拦截器添加到OkHttp并构建Retrofit

所有通过这个OkHttpClient构建的Retrofit实例,发起的请求都会被自动统计:

// 构建配置好拦截器的OkHttpClient
val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(TimingInterceptor()) // 添加应用拦截器
    // 其他常用配置:超时时间、缓存等
    .connectTimeout(30, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .writeTimeout(30, TimeUnit.SECONDS)
    .build()

// 用这个OkHttpClient创建Retrofit实例
val retrofit = Retrofit.Builder()
    .baseUrl("https://your-api-domain.com/")
    .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create()) // 根据你的序列化库调整
    .build()

进阶:更精细的阶段耗时统计(可选)

如果除了总时长,还需要了解DNS解析、连接建立、请求发送等细分阶段的耗时,可以使用OkHttp的EventListener:

class DetailedTimingEventListener : EventListener() {
    private var callStartTime = 0L
    private var dnsStartTime = 0L
    private var connectStartTime = 0L

    override fun callStart(call: Call) {
        callStartTime = System.nanoTime()
        super.callStart(call)
    }

    override fun dnsStart(call: Call, domainName: String) {
        dnsStartTime = System.nanoTime()
        super.dnsStart(call, domainName)
    }

    override fun dnsEnd(call: Call, domainName: String, inetAddressList: List<InetAddress>) {
        val dnsElapsedMs = (System.nanoTime() - dnsStartTime) / 1_000_000
        Log.d("DetailedStats", "DNS解析耗时:$dnsElapsedMs ms")
        super.dnsEnd(call, domainName, inetAddressList)
    }

    override fun connectStart(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy) {
        connectStartTime = System.nanoTime()
        super.connectStart(call, inetSocketAddress, proxy)
    }

    override fun connectEnd(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy, protocol: Protocol?) {
        val connectElapsedMs = (System.nanoTime() - connectStartTime) / 1_000_000
        Log.d("DetailedStats", "连接建立耗时:$connectElapsedMs ms")
        super.connectEnd(call, inetSocketAddress, proxy, protocol)
    }

    override fun callEnd(call: Call) {
        val totalElapsedMs = (System.nanoTime() - callStartTime) / 1_000_000
        Log.d("DetailedStats", "总请求耗时:$totalElapsedMs ms")
        super.callEnd(call)
    }
}

然后在OkHttp Builder中添加这个EventListener:

val okHttpClient = OkHttpClient.Builder()
    .eventListenerFactory { DetailedTimingEventListener() }
    // 其他配置...
    .build()

注意事项

  • 确保所有Retrofit实例都使用同一个配置了拦截器/EventListener的OkHttpClient,这样才能覆盖所有请求的统计。
  • 如果是Java项目,只需要把Kotlin代码转换成对应的Java语法即可,逻辑完全一致。
  • 统计的数据可以根据业务需求扩展,比如按接口分类统计平均耗时、错误率等,只需要在拦截器中添加对应的逻辑即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:36:43