okhttp interceptor拦截器简单介绍

本文分析了在使用OkHttp3.10时遇到的SocketTimeoutException,重点讲解了Interceptor的工作原理,包括RetryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、ConnectInterceptor和CallServerInterceptor在请求处理中的角色和功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分析基于okhttp3.10

背景

起因在于,最近应用团队找我们分析网络问题,提示SocketTimeoutException,其实很明显是无法接收到服务端的相应,此时socket连接应是正常建立的。但是对于okhttp的源码不熟悉,不清楚什么日志代表socket已经建立。

06-16 07:36:00.312 22995 23067 W System.err: java.net.SocketTimeoutException: timeout
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http2.Http2Stream S t r e a m T i m e o u t . n e w T i m e o u t E x c e p t i o n ( H t t p 2 S t r e a m . j a v a : 593 ) 06 − 1607 : 36 : 00.3142299523067 W S y s t e m . e r r : a t o k h t t p 3. i n t e r n a l . h t t p 2. H t t p 2 S t r e a m StreamTimeout.newTimeoutException(Http2Stream.java:593) 06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http2.Http2Stream StreamTimeout.newTimeoutException(Http2Stream.java:593)061607:36:00.3142299523067WSystem.err:atokhttp3.internal.http2.Http2StreamStreamTimeout.exitAndThrowIfTimedOut(Http2Stream.java:601)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http2.Http2Stream.takeResponseHeaders(Http2Stream.java:146)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http2.Http2Codec.readResponseHeaders(Http2Codec.java:120)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:75)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
06-16 07:36:00.315 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
06-16 07:36:00.315 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
06-16 07:36:00.315 22995 23067 W System.err: at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
06-16 07:36:00.315 22995 23067 W System.err: at okhttp3.RealCall.execute(RealCall.java:69)
06-16 07:36:00.315 22995 23067 W System.err: at com.gala.sdk.player.RecordManagerImpl.doRequestWithHeader(RecordManagerImpl.java:1348)

正文

当调用者发起请求时,不管是同步请求还是异步请求,都会通过RealCall.java的execute()方法调用同一个getResponseWithInterceptorChain()

首先,在请求真正发出去之前,会现经过一些interceptor(拦截器),这其实是面向切面编程(AOP: Aspect Oriented Programming)的概念,顺序如下:

  1. 用户自定义拦截器
  2. RetryAndFollowUpInterceptor
  3. BridgeInterceptor
  4. CacheInterceptor
  5. ConnectInterceptor
  6. CallServerInterceptor

其中ConnectInterceptor会创建socket(也可能是从connectionPool中复用)

Interceptor介绍

Interceptor顾名思义,就是拦截器的意思。由面向切面编程(AOP: Aspect Oriented Programming)概念引入,okhttp引入AOP,旨在让用户可以简单方便的对request或者response在发送给Server前或者在返回给终端用户前做一些处理。比如,我们需要在每个请求头部加入Authorization字段(添加token校验),那么我们只需要定义一个interceptor,在每个HTTP请求发出去之前为其添加相关头部字段。

okhttp在其内部已经提供了几个Interceptor:

  1. RetryAndFollowUpInterceptor
  2. BridgeInterceptor
  3. CacheInterceptor
  4. ConnectInterceptor
  5. CallServerInterceptor

下面我们对这些拦截器详细解释下。

RetryAndFollowUpInterceptor

This interceptor recovers from failures and follows redirects as necessary. It may throw an IOException if the call was canceled.

代码的注释已经非常清晰:RetryAndFollowUpInterceptor

这个拦截器主要处理请求的重试和跟踪重定向。当请求失败时(例如,由于网络问题或服务器返回非成功状态码),RetryAndFollowUpInterceptor负责决定是否应该重试请求以及如何处理重定向。

BridgeInterceptor

用于将用户的高级请求转换为底层HTTP请求。它添加了一些必要的HTTP头,例如User-Agent(用户代理),以确保请求在HTTP层面上正确发送到服务器。

通常我们使用okhttp时,只填充业务相关的一些字段,而剩下的一些HTTP Header,就在这里由okhttp进行填充。

CacheInterceptor

用于处理缓存相关的操作。它可以在请求之前检查缓存是否包含所需的响应,并在适用的情况下返回缓存的响应。如果没有缓存或需要刷新缓存,它将请求传递给下一个拦截器,以获取新的响应。

目前仅支持GET请求的缓存,其他诸如POST/PUT/DELETE等都不支持。

缓存拦截器有个前提:客户端和服务器不能禁用缓存功能,如果禁用,这个interceptor实际上就不起作用了

另外,如果缓存过期,那么需要再次发出请求。

private CacheStrategy getCandidate() {
    if (requestCaching.noCache() || hasConditions(request)) {
        return new CacheStrategy(request, null);
    }
    if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
        Response.Builder builder = cacheResponse.newBuilder();
        if (ageMillis + minFreshMillis >= freshMillis) {
          builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
        }
        long oneDayMillis = 24 * 60 * 60 * 1000L;
        if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
          builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
        }
        return new CacheStrategy(null, builder.build());
    }
    // ... ...
}

ConnectInterceptor

负责建立与服务器的连接,包括TLS握手(如果需要)。它执行DNS解析、TCP连接和TLS握手等网络操作。

CallServerInterceptor

是发送请求到服务器并获取响应的拦截器。它将请求发送到服务器,并处理服务器响应,包括响应头和响应体。如果缓存可用,它还可以缓存响应。

总结

这些拦截器在OkHttp中按照特定的顺序组成一个拦截器链,每个拦截器都负责不同的任务。这种设计允许OkHttp在处理HTTP请求和响应时实现高度的可扩展性和灵活性。每个拦截器都有其特定的责任,确保请求和响应按照期望的方式进行处理。

### OkHttp拦截器的创建与使用 #### 创建自定义拦截器 为了在 OkHttp 请求过程中加入特定逻辑处理,可以创建自定义拦截器。通过继承 `Interceptor` 接口并重写其唯一的抽象方法 `intercept()` 来实现。 ```java public class MyCustomInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); // 修改请求头或其他属性前的操作 Request modifiedRequest = originalRequest.newBuilder() .header("My-Custom-Header", "HeaderValue") .build(); long startTime = System.nanoTime(); // 记录开始时间 try (Response response = chain.proceed(modifiedRequest)) { // 将修改后的请求传递给下一个拦截器 // 响应返回后可在此处做日志记录等操作 long endTime = System.nanoTime(); Log.d("OkHttp", String.format("Received response for %s in %.1fms", response.request().url(), (endTime - startTime) / 1e6d)); return response; } } } ``` #### 添加到 OkHttpClient 实例中 一旦实现了自己的拦截器类,则可以通过构建 `OkHttpClient.Builder` 对象来注册这些拦截器: ```java OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new MyCustomInterceptor()) // 添加自定义拦截器 .build(); ``` 对于需要在网络层面上工作的场景(比如添加公共参数),应该考虑使用应用级拦截器;而对于想要针对单个请求做一些特殊处理的情况,则更适合采用链路级别的拦截器[^4]。 #### 日志拦截器的应用实例 除了编写自定义业务逻辑外,还可以方便地利用官方提供的 Logging 拦截器来进行调试目的的日志打印功能: ```kotlin // Kotlin 示例代码 val logging = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY // 设置日志级别为 BODY 显示全部内容 } val okHttpClient = OkHttpClient.Builder() .addInterceptor(logging) .build() ``` 上述配置会使得每一次 HTTP 调用的相关信息都被输出至控制台,这对于开发阶段排查问题非常有帮助[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值