如何测量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
相关产品推荐
相关产品推荐

