Android Okhttp3 拦截器源码解析

 在 OkHttp 中,拦截器(Interceptor)是一种强大的机制,用于在发送请求和接收响应的过程中,对请求和响应进行拦截、处理和修改。拦截器可以在网络请求的不同阶段进行操作,允许开发者对请求进行修改、记录日志、添加头部信息、处理重定向、处理错误等。

OkHttp 的拦截器是基于责任链模式设计的,每个拦截器都可以对请求进行拦截并对其进行处理,然后将请求传递给下一个拦截器,形成一个拦截器链。这样,在发送请求和接收响应的过程中,请求会依次经过拦截器链中的每个拦截器,每个拦截器都有机会对请求进行操作。


 用 Okhttp 执行网络后,会调用到 getResponseWithInterceptorChain() 方法:

package okhttp3;

final class RealCall implements Call {
  // ....

  Response getResponseWithInterceptorChain() throws IOException {
    // 构建一个完整的拦截器堆栈。
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());                     // 自定义拦截器
    interceptors.add(retryAndFollowUpInterceptor);                  // 重试和重定向拦截器
    interceptors.add(new BridgeInterceptor(client.cookieJar()));    // 桥接拦截器
    interceptors.add(new CacheInterceptor(client.internalCache())); // 缓存拦截器
    interceptors.add(new ConnectInterceptor(client));               // 连接拦截器
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));      // 请求服务拦截器

    // ....
    return chain.proceed(originalRequest);
  }
}

 所有的拦截器都存储在一个 List 集合中,在之后会进行顺序调用,顺序:

自定义拦截器 -> 重试和重定向拦截器 -> 桥接拦截器 -> 缓存拦截器 -> 连接拦截器 -> 请求服务拦截器


 1、重试和重定向拦截器:

package okhttp3.internal.http;

public final class RetryAndFollowUpInterceptor implements Interceptor {
  // ....

  @Override public Response intercept(Chain chain) throws IOException {
    // ....

    while (true) {
      Response response;
      try {
        response = realChain.proceed(request, streamAllocation, null, null); // 真正干活的
      } catch (RouteException e) {
        // recover 这个方法判断是否重试
        if (!recover(e.getLastConnectException(), false, request)) {
          throw e.getLastConnectException();
        }
        continue;
      } catch (IOException e) {
        // recover 这个方法判断是否重试
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, requestSendStarted, request)) throw e;
        continue;
      } finally {
        // 释放
      }

      // 根据返回的结果判断是否需要重定向
      Request followUp = followUpRequest(response);
      if (followUp == null) { // 不需要重定向,直接返回
        return response;
      }
      
      // ...
  }

}

不难看出,当中有一个 while 死循环,先执行 RealInterceptorChain.proceed(),若出现了异常再进行重试(经过了 recover 方法中一系列的判断后满足重试条件后),正常请求后对 Response 进行判断是否需要重定向

那这个 RealInterceptorChain.proceed() 做了什么呢:

package okhttp3.internal.http;

/**
 * 一个具体的拦截器链,它包含整个拦截器链:所有应用程序拦截器,OkHttp核心,所有网络拦截器。
 * 最后是网络调用者。
 */
public final class RealInterceptorChain implements Interceptor.Chain {
  // ....

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    // ....

    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout); // 组装chain
    Interceptor interceptor = interceptors.get(index); // 获取下一个拦截器 
    Response response = interceptor.intercept(next);   // 调用下一个拦截器

    // ....
    return response;
  }
}

这里我只截取了关键的代码,经过 chian.proceed() 方法调用下一个拦截器:


2、桥接拦截器:

这个拦截器的主要目的就是帮我们补全一些网络请求的信息,还有对响应体 Gzip 压缩判断

package okhttp3.internal.http;

/**
 * 从应用程序代码到网络代码的桥梁。首先,它根据用户请求构建网络请求。
 * 然后它继续调用网络。最后,它根据网络响应构建用户响应。
 */
public final class BridgeInterceptor implements Interceptor {

  @Override public Response intercept(Chain chain) throws IOException {
    // ...

      if (contentType != null) { // 补充 contentType 
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) { // 补充请求体长度
        requestBuilder.header("Content-Length", Long.toString(contentLength));
      } else { // 拿不到请求体的长度,分块编码
        requestBuilder.header("Transfer-Encoding", "chunked");
      }

    if (userRequest.header("Host") == null) { // 补充 Host
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }
    if (userRequest.header("Connection") == null) { // 补充 Connection
      requestBuilder.header("Connection", "Keep-Alive");
    }

    // 允许服务器用 Gzip 压缩之后再响应回来
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      requestBuilder.header("Accept-Encoding", "gzip");
    }

    // 加载 Cookie 数据
    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

    if (userRequest.header("User-Agent") == null) { // 加入 okhttp 的版本
      requestBuilder.header("User-Agent", Version.userAgent());
    }

    Response networkResponse = chain.proceed(requestBuilder.build()); // 拿到响应结果

    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);
    // ... (若同意 Gzip 压缩,则进行 Gzip 解压)

    return responseBuilder.build();
  }
}

 经过 chian.proceed() 方法调用下一个拦截器:


 3、缓存拦截器

网络的缓存比较复杂,在 okhttp 当中,对缓存判断封装到了 CacheStrategy 缓存策略里面

package okhttp3.internal.cache;

public final class CacheInterceptor implements Interceptor {

  @Override public Response intercept(Chain chain) throws IOException {
    // ....

    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); // 经过了缓存策略判断后返回的对象
    Request networkRequest = strategy.networkRequest; // 请求
    Response cacheResponse = strategy.cacheResponse;  // 缓存响应

    // ... (判空处理)

    Response networkResponse = null;
    try {
      networkResponse = chain.proceed(networkRequest); // 拿到响应结果
    } finally {
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body()); // 释放
      }
    }

    if (cacheResponse != null) { // 如果服务器响应 304 且有缓存,则直接使用缓存
      if (networkResponse.code() == 304) {
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();
        cache.trackConditionalCacheHit();
        cache.update(cacheResponse, response);
        return response;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }

    // ...
    return response;
  }
}

经过 chian.proceed() 方法调用下一个拦截器: 


 4、连接拦截器

这个拦截器就是帮我们获取一个连接,代码比较少:

package okhttp3.internal.connection;

// 打开到目标服务器的连接,并继续到下一个拦截器。
// 网络可以用于返回的响应,或者用条件GET验证缓存的响应。
public final class ConnectInterceptor implements Interceptor {

  @Override public Response intercept(Chain chain) throws IOException {
    // ...

    HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks); // 查找链接
    RealConnection connection = streamAllocation.connection(); // 获取连接

    return realChain.proceed(request, streamAllocation, httpCodec, connection);

  }
}

获取连接的核心代码是在 StreamAllocation.findConnection() 上,比较多(未完待续)

经过 chian.proceed() 方法调用下一个拦截器:


 5、请求服务拦截器

最后一个拦截器,对服务器进行网络调用

package okhttp3.internal.http;

public final class CallServerInterceptor implements Interceptor {

  @Override public Response intercept(Chain chain) throws IOException {
    // ...

    httpCodec.writeRequestHeaders(request); // 往服务器发送请求头

    if (responseBuilder == null) {
      responseBuilder = httpCodec.readResponseHeaders(false); // 解析服务器响应数据
    }

    Response response = responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    // 如果连接断开,证明不是长连接
    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      streamAllocation.noNewStreams(); // 不加入连接池
    }

    return response;
  }
}

请求服务拦截器处理完毕后,整个请求过程就完成了。接下来,响应将被返回给调用方进行进一步处理,包括解析响应数据、处理状态码、处理错误等。

猜你喜欢

转载自blog.csdn.net/weixin_47592544/article/details/131466384