OkHttp源码学习笔记 (二) 拦截器源码读解

本文详细剖析了OkHttp框架中拦截器的工作原理,包括重试拦截器、请求信息处理拦截器、缓存拦截器和请求服务器拦截器。通过责任链设计模式,每个拦截器都有决定处理事件的能力。重试拦截器处理连接问题,请求信息处理拦截器添加请求头,缓存拦截器根据网络和缓存情况决定响应,而请求服务器拦截器则是最后执行实际的网络请求并组装响应体。
摘要由CSDN通过智能技术生成

前言

在上一篇文章中 主线流程源码分析 中, 我们了解到了OkHttp框架的流程和原理. 并在最后留下了一个知识分块, 那就是 "拦截器"

我们在源码中得知到Response响应体其实是经过一个由责任链设计模式设计出来的方法中getResponseWithInterceptorChain()生成并返回的, 而本文的目标就是要剖析这拦截器到底做了什么

 

开始分析

先看看这个方法有什么

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    // 开发者自定义拦截器,在OkHttpClient构建的时候设置的
    interceptors.addAll(client.interceptors());
    // 重试与重定向拦截器
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    // 请求信息处理拦截器
    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));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
      Response response = chain.proceed(originalRequest);
      if (transmitter.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
      }
      return response;
    } catch (IOException e) {
      calledNoMoreExchanges = true;
      throw transmitter.noMoreExchanges(e);
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null);
      }
    }
  }
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    @Nullable Connection connection();

    Call call();

    int connectTimeoutMillis();

    Chain withConnectTimeout(int timeout, TimeUnit unit);

    int readTimeoutMillis();

    Chain withReadTimeout(int timeout, TimeUnit unit);

    int writeTimeoutMillis();

    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }
}

在这里基本得知 有四个核心拦截器. 分别为"重试/重定向拦截器" "请求信息处理拦截器" "缓存拦截器" 和 "连接服务器请求拦截器".

通过拦截器设计模式 每个拦截器自身都有决定是否处理事件以及具体逻辑的能力, 我们先一步一步来看看.

在代码中 拦截器集合被添加完之后, 会生成一个Intercepor内部类Chain的对象. 这Chain实际上是一个接口, 所以按照代码逻辑来分析, 真正的实现类其实是RealInterceptorChain. 而Response则是从Chain的proceed方法得来的. 所以我们现在看看RealInterceptorChain里面的proceed方法干了什么

public final class RealInterceptorChain implements Interceptor.Chain {
  // RealCall传进来的拦截器集合
  private final List<Interceptor> interceptors;
  private final Transmitter transmitter;
  private final @Nullable Exchange exchange;
  // 当前下标,如果是RealCall传过来的就是0,如果是自身递归执行的就是不断+1
  private final int index;
  private final Request request;
  private final Call call;
  private final int connectTimeout;
  private final int readTimeout;
  private final int writeTimeout;
  private int calls;

  public RealInterceptorChain(List<Interceptor> interceptors, Transmitter transmitter,
      @Nullable Exchange exchange, int index, Request request, Call call,
      int connectTimeout, int readTimeout, int writeTimeout) {
    this.interceptors = interceptors;
    this.transmitter = transmitter;
    this.exchange = exchange;
    this.index = index;
    this.request = request;
    this.call = call;
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    this.writeTimeout = writeTimeout;
  }

  @Override public @Nullable Connection connection() {
    return exchange != null ? exchange.connection() : null;
  }

  @Override public int connectTimeoutMillis() {
    return connectTimeout;
  }

  @Override public Interceptor.Chain withConnectTimeout(int timeout, TimeUnit unit) {
    int millis = checkDuration("timeout", timeout, unit);
    return new RealInterceptorChain(interceptors, transmitter, exchange, index, request, call,
        millis, readTimeout, writeTimeout);
  }

  @Override public int readTimeoutMillis() {
    return readTimeout;
  }

  @Override public Interceptor.Chain withReadTimeout(int timeout, TimeUnit unit) {
    int millis = checkDuration("timeout", timeout, unit);
    return new RealInterceptorChain(interceptors, transmitter, exchange, index, request, call,
        connectTimeout, millis, writeTimeout);
  }

  @Override public int writeTimeoutMillis() {
    return writeTimeout;
  }

  @Override public Interceptor.Chain withWriteTimeout(int timeout, TimeUnit unit) {
    int millis = checkDuration("timeout", timeout, unit);
    return new RealInterceptorChain(interceptors, transmitter, exchange, index, request, call,
        connectTimeout, readTimeout, millis);
  }

  public Transmitter transmitter() {
    return transmitter;
  }

  public Exchange exchange() {
    if (exchange == null) throw new IllegalStateException();
    return exchange;
  }

  @Override public Call call() {
    return call;
  }

  @Override public Request request() {
    return request;
  }

  @Override public Response proceed(Request request) throws IOException {
    return proceed(request, transmitter, exchange);
  }

  public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
      throws IOException {
    // 一开始传进来的index为0 所以肯定不会抛出异常
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // 在这里new一个新的节点, 其实就是链路中的下一个RealInterceptorChain, 犹如递归似的, 其中index自身+1处理
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
        index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    // 根据当前下标取出对应的拦截器
    Interceptor interceptor = interceptors.get(index);
    // 用对应的拦截器,把下一个节点丢进去进行拦截处理
    Response response = interceptor.intercept(next);

    // 把得到的response进行返回
    return response;
  }
}

这似乎相对比较抽象, 那我们捋一下思路先, 按照okhttp的源码规定来看, 4个拦截器其实是按顺序添加的. "1重试 -> 2请求信息 -> 3缓存 -> 4请求" 就相当于链路的生成是从1到4往下走的, 而走的过程中, 会对response进行处理加工/返回. 并最终从后往前执行. 当然 并不一定是4->3->2->1 , 假如是有缓存拦截器, 已经拦截到有response, 此时就会直接往回调, 而不会专门去请求了.

 

具体我们针对这4个拦截器进行逐一分析吧.

 

1.重试拦截器

重试拦截器里面其实是有一个while死循环, 如果请求过程中出了连接问题, 那么就会自行恢复连接并尝试往后传递, 直到整个请求结束后都没出问题为止

public final class RetryAndFollowUpInterceptor implements Interceptor {

  // 重试代码比较多 所以砍了一大堆看起来不大相干的代码了 只留一些有用的


  // 在这里开始做拦截
  @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    // 这里弄了个死循环
    while (true) {
     // 响应信息
     Response response;
      try {
        // 这里往后传递
        response = realChain.proceed(request, transmitter, null);
        success = true;
      } catch (RouteException e) {
        // 这里就是在请求过程中, 连接失败了, 就会走recover方法进行尝试重连
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), transmitter, false, request)) {
          throw e.getFirstConnectException();
        }
        // 然后本次循环结束 再循环一次
        continue;
      } catch (IOException e) {
        // 这里就是在请求过程中, 连接失败了, 就会走recover方法进行尝试重连
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, transmitter, requestSendStarted, request)) throw e;
        // 然后本次循环结束 再循环一次
        continue;
      } finally {
        // The network call threw an exception. Release any resources.
        if (!success) {
          transmitter.exchangeDoneDueToException();
        }
      }

      Exchange exchange = Internal.instance.exchange(response);
      Route route = exchange != null ? exchange.connection().route() : null;
      Request followUp = followUpRequest(response, route);

      if (followUp == null) {
        if (exchange != null && exchange.isDuplex()) {
          transmitter.timeoutEarlyExit();
        }
        return response;
      }

      RequestBody followUpBody = followUp.body();
      if (followUpBody != null && followUpBody.isOneShot()) {
        return response;
      }
    }
  }

2.请求信息处理拦截器

通过这个信息拦截器, 会对response进行各种请求头的添加. 添加完之后, 继续往后传递.

public final class BridgeInterceptor implements Interceptor {

  // 在这里接收拦截
  @Override public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();
    
    RequestBody body = userRequest.body();
    // 取得请求体之后, 会对请求体进行各种的header信息操作, 也就是说可以在这里插入请求信息
    if (body != null) {
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }
    }

    ...
    ...
    ...

    // 同样 也是在这里往下一个拦截器传递
    Response networkResponse = chain.proceed(requestBuilder.build());

    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);

    if (transparentGzip
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
        && HttpHeaders.hasBody(networkResponse)) {
      GzipSource responseBody = new GzipSource(networkResponse.body().source());
      Headers strippedHeaders = networkResponse.headers().newBuilder()
          .removeAll("Content-Encoding")
          .removeAll("Content-Length")
          .build();
      responseBuilder.headers(strippedHeaders);
      String contentType = networkResponse.header("Content-Type");
      responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
    }

    //处理完之后 返回
    return responseBuilder.build();
  }

  /** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */
  private String cookieHeader(List<Cookie> cookies) {
    StringBuilder cookieHeader = new StringBuilder();
    for (int i = 0, size = cookies.size(); i < size; i++) {
      if (i > 0) {
        cookieHeader.append("; ");
      }
      Cookie cookie = cookies.get(i);
      cookieHeader.append(cookie.name()).append('=').append(cookie.value());
    }
    return cookieHeader.toString();
  }
}

3.缓存拦截器

这里面 就会根据当前到底有没有网络/有没有缓存进行各种综合考虑, 比如没网又没缓存, 就会返回报错response. 如果没网有缓存 ,就取缓存response. 如果有网, 那就会往后传递(下一步就是网络请求拦截器), 如果有缓存更新 那就会更新一下再返回response.

public final class CacheInterceptor implements Interceptor {
  final @Nullable InternalCache cache;

  public CacheInterceptor(@Nullable InternalCache cache) {
    this.cache = cache;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    // 获取网络请求体, 如果当前没网会为空
    Request networkRequest = strategy.networkRequest;
    // 缓存响应体, 如果当前没缓存则会为空
    Response cacheResponse = strategy.cacheResponse;

    // 如果当前又没网,缓存又没存,直接就当场返回个错误的reponse回去了.
    // If we're forbidden from using the network and the cache is insufficient, fail.
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }
    
    // 如果单纯没网, 那就用缓存的请求响应体返回回去.
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    // 如果有网络,那就还是会往后传递
    Response networkResponse = null;
    try {
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

    // 如果是有缓存的,那就判断下是否有改动的
    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        // 如果是有改动的话,那就会更新一下缓存对象 再返回回去
        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();

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        cache.trackConditionalCacheHit();
        cache.update(cacheResponse, response);
        return response;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }
  }
}

4.请求服务器拦截器

这个拦截器就是最终的拦截器, 也就是请求并组装响应体的拦截器. 注意这是最后一个拦截器, 所以不会再有proceed下一步传递的操作了.

public final class CallServerInterceptor implements Interceptor {

  // 在这里接收拦截
  @Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Exchange exchange = realChain.exchange();
    Request request = realChain.request();

    ...
    ...
    ...

    // 就是在这里 根据exchange以及request等信息 创建response对象
    Response response = responseBuilder
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    // 然后后面的这些 应该就是对请求回来的信息进行各种判断和组装了

    int code = response.code();
    if (code == 100) {
      // server sent a 100-continue even though we did not request one.
      // try again to read the actual response
      response = exchange.readResponseHeaders(false)
          .request(request)
          .handshake(exchange.connection().handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();

      code = response.code();
    }

    exchange.responseHeadersEnd(response);

    if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
      response = response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build();
    }

    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      exchange.noNewExchangesOnConnection();
    }

    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    // 最终就可以在这里返回response
    return response;
  }
}

 

其实上述描述中 应该还有不少地方是有遗漏的 具体每个拦截器的每一行代码还没专门去细致读解. 主要是为了了解这些拦截器之间的关系 以及负责的职责.

 

其中有很多地方是没注意到的, 甚至理解可能会有误, 还请各位读者dalao们多多指点多多批评.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值