OkHttp之getResponseWithInterceptorChain(一)

目录

 

前言

拦截器的加入

RealInterceptorChain

RetryAndFollowUpInterceptor

1)创建StreamAllocation

2)其他几个步骤

3)总结

BridgeInterceptor


前言

前面介绍了OkHttp之Dispatcher, Dispatcher主要就是对异步请求进行分发和执行,那么对于OkHttp到底是怎么进行完成忘了请求的呢?主要实现就是getResponseWithInterceptorChain()这个里面的内容。

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    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));

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

    return chain.proceed(originalRequest);
  }

拦截器的加入

从源码中看到就是添加了一系列的拦截器,依次加入的拦截器为:

1)用户自定义的拦截器,注意要实现Interceptor.Chain接口,并且要取调用下一个拦截器。可以在请求服务器之前做一些自己的逻辑处理。

2)RetryAndFollowUpInterceptor:失败之后自动重链和重定向

3)BridgeInterceptor:数据转换,将用户设置的Request转换成网络请求所需要的请求,传到下一个拦截器中,并将下一个拦截器返回的结果转换成RealResponseBody返回给上一个拦截器。

4)CacheInterceptor:缓存。读取缓存直接返回和更新缓存,并将其下一个拦截器返回的结果返回给上一个拦截器。

5)ConnectInterceptor:创建一个链接,并将其下一个拦截器返回的结果返回给上一个拦截器

6)CallServerInterceptor:向服务器请求数据,完成请求,并返回给上一个拦截器。

最后将这些拦截器都传入到RealInterceptorChain,通过chain.proceed()来完成整个请求过程。而这些拦截器中采用的是责任链模式,一个拦截器会去调用下一个拦截器的intercept()或.proceed()来完成最后的请求。

我们就分别分析下各个拦截器。

RealInterceptorChain

拦截器执行的起始。在将一系列的拦截器加入到interceptors集合中之后,从这里的proceed()开始执行。

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    // .....省略代码
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    // .....省略代码
    return response;
  }

我们发现就是从 interceptors集合中取出加进去的第一个拦截器,然后执行拦截器的intercept()的方法。从上面的分析中我们可以看到除去用户自定义的拦截器,那么就是RetryAndFollowUpInterceptor了,所以就调用了RetryAndFollowUpInterceptor的intercept()方法,同时将RetryAndFollowUpInterceptor返回的Response返回给RealInterceptorChain。

RetryAndFollowUpInterceptor

@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Call call = realChain.call();
    EventListener eventListener = realChain.eventListener();
//1)创建StreamAllocation
    StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
    this.streamAllocation = streamAllocation;

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
 //....省略代码
      Response response;
      boolean releaseConnection = true;
      try {
//2)调用下一个拦截器CacheInterceptor
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
      //....省略代码
      } finally {
      //....省略代码
      }
      //上一次的response若存在,则将上一次的response的body置为null,赋值到response
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }
//3)根据response的响应码和响应头,查看是否需要重定向,并返回获取request
      Request followUp;
      try {
        followUp = followUpRequest(response, streamAllocation.route());
      } catch (IOException e) {
        streamAllocation.release();
        throw e;
      }
//4)如果没有request返回,则不需要重定向,直接返回
      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }

      closeQuietly(response.body());
//5)查看是否超出重链的最大次数,抛出异常
      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }
//....省略代码
//6)检查如果不是相同的连接,则重建
      if (!sameConnection(response, followUp.url())) {
        streamAllocation.release();
        streamAllocation = new StreamAllocation(client.connectionPool(),
            createAddress(followUp.url()), call, eventListener, callStackTrace);
        this.streamAllocation = streamAllocation;
      }
//....省略代码
//7)重新设置requst,并把当前的response赋值给到priorResponse,继续while循环
      request = followUp;
      priorResponse = response;
    }
  }

从源码的注释中可以看到,该拦截器主要用来失败之后重链或者重定向。

1)创建StreamAllocation

    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Call call = realChain.call();
    EventListener eventListener = realChain.eventListener();
//创建StreamAllocation
    StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
    this.streamAllocation = streamAllocation;

在拦截器中创建StreamAllocation。而StreamAllocation主要用来建立执行HTTP请求所需要网络设施的组件。即完成为每个请求(call)来寻找连接connection并建立传输流codec。所以在StreamAllocation中包含着请求call(Call)、传输流codec(HttpCodec)、连接connection(RealConnection)、连接池connectionPool(ConnectionPool),对应的获取流的方法为newStream(),找到合适连接方法findConnection()以及完成请求任务之后的一些关闭流对象finish()、终止、取消以及释放资源的方法。

简单介绍下StreamAllocation的几个关键代码,创建stream的创建过程如下:对应着newStream()

 public HttpCodec newStream(
      OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
 //.....省略代码
    try {
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
      HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
      synchronized (connectionPool) {
        codec = resultCodec;
        return resultCodec;
      }
    } 
 //.....省略代码
  }

在newStream()中主要就是通过findHealthyConnection()找到一个可用的连接,并且进行建立连接,而该方法返回的流HttpCodec,如果是Http1.1/1.0则返回的是Http1Codec,如果是Http2.0则返回的是Http2Codec。

1)流Codec,假设返回的是Http1Codec

在RealConnection中的establishProtocol()中会根据不同的情况确定是使用Http1.0/1.1协议还是Http2.0协议。这里面提供了一些网络请求的基本方法,像发送请求的时候写入请求头部、创建请求体等,后面结合CallServerInterceptor具体看下里面的各个方法的作用。

2)简单的说下findHealthyConnection()。最终调用的就是findConnection()

  private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
//.......省略代码
    // 前面通过一系列的逻辑找到一个可用的connection,建立起连接
    result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
        connectionRetryEnabled, call, eventListener);

//.......省略代码
}

该方法主要就是找到可用的connection,建立起连接。首先会查看已经存在的connection是否可用,如果可用,则直接返回;否则就从connectionPool中找到可用的connection,如果还是找不到则通过new RealConnection来创建connection,通过这种方法创建的连接需要通过acquire关联到connection.allocations上 。

我们可以看到在result.connect()调用的过程中,已经将请求call传入到了connection中。另外还要注意一点,这个里面提到的conectionPool是从上一个拦截器中传入的OkHttpClient中获取的,而conectionPool在创建OkHttpClient中被实例化的。所以如果OkHttpClient全局只保持一个实例的化,那么这个链接池conectionPool也只有这一个实例。

这个newStream()的真正调用就在ConnectInterceptor连接器中,代码后面在介绍。

综上,在RetryAndFollowUpInterceptor这个拦截器中仅仅是创建了StreamAllocation对象,把这个对象传入了下一个拦截器。而StreamAllocation负责为该次请求找到可用的连接,并建立连接。

2)其他几个步骤

从其他几个过程中看来,这里就是建立了一个while(true)的循环来进行重试。

(1)如果可以正常调用下一个拦截器,则中断该循环,进入到下一个拦截器中;

(2)如果不能去调用到下一个拦截器,那么就要看看是否保存有前一次的priorResponse,构建一个body为null的response,然后判断followUp请求是否需要重定向,若不需要则直接将该response返回

(3)如果需要的化,则去检查重定向次数是否超出了最大次数,如果是,则抛出异常

(4)否则就去检查是否有相同的链接,如果不是则需要重新创建StreamAllocation

(5)重新设置request和priorResponse进行下一次重试。

3)总结

RetryAndFollowUpInterceptor这个拦截器就是实例化了StreamAllocation对象传入到下一个拦截器中,并且保存上一次的response,不断的根据response返回的响应码来确定是否进行重试。

RetryAndFollowUpInterceptor拦截器通过RealInterceptorChain的 realChain.proceed(),因为此时RealInterceptorChain的实例是从之前调用RetryAndFollowUpInterceptor的时候传入的,所以此时RealInterceptorChain中会调用到了BridgeInterceptor。

BridgeInterceptor

就是将用户设置的数据转换成网络请求所用的数据,并调用到下一个拦截器,并将返回的结果根据是否需要gzip来进行压缩。

@Override public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();

    RequestBody body = userRequest.body();
    if (body != null) {
//设置request的header
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }
//......省略代码 就是设置request的一些header
//加载cookie
    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }
//调用到下一个拦截器
    Response networkResponse = chain.proceed(requestBuilder.build());
//将信息保存到Cookie中
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
//对拦截器返回的response进行处理。如果支持gzip压缩,则有Okio处理
    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();
  }

1)主要设置了header的Content-Type、Content-Length、Transfer-Encoding、Host、Connection为Keep-Alive、Accept-Encoding、Cookie、User-Agent

2)判断是否需要加载Cookie

3)调用下一个拦截器,返回networkResponse

4)若有Cookie则将信息保存到Cookie中

5)判断networkResponse是否支持gzip压缩,如果支持则通过Okio进行压缩

6)否则直接返回response给上一个拦截器。

这个CookieJar是通过构造函数传入的

  public BridgeInterceptor(CookieJar cookieJar) {
    this.cookieJar = cookieJar;
  }

显然在RealCall在将 BridgeInterceptor加入到拦截器集合的时候,从OkHttpClient中获取的,跟踪到源码中发现,其实OkHttp默认的是没有Cookie的

    public Builder() {
 //....省略代码
      cookieJar = CookieJar.NO_COOKIES; 
//....省略代码
}

我们看到BridgeInterceptor同样也是通过调用RealInterceptorChain的 realChain.proceed(),调用到了下一个拦截器CacheInterceptor

其他拦截器见OkHttp之getResponseWithInterceptorChain(二)

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值