okHttp拦截器分析(一)

之前分析了okHttp的同步跟异步流程分析,都是经过层层封装,然后卡在拦截器这里,关键封装请求头,建立连接,获取数据的部分全在拦截器这部分,由于拦截器较多,分析同步,异步的时候并没有详细解析,在这里,一一解析。

《okHttp拦截器分析(一)》 image.png

回顾下getResponseWithInterceptorChain()方法:

  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
  ......
 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);
  }

interceptors是list集合,首先就把自定义的拦截器add进去了,在这里,假装没有写自定义拦截器,来看看retryAndFollowUpInterceptor。看看注释:This interceptor recovers from failures and follows redirects as necessary。很明显,这个拦截器是用来负责失败重试以及重定向的,我们来看看关键代码:

public final class RetryAndFollowUpInterceptor implements Interceptor {
  ......
  ......
 @Override public Response intercept(Chain chain) throws IOException {
    //获取请求体request
    Request request = chain.request();
   //获取拦截器链对象,这个很重要,是用来运行完拦截器集合的关键(内部的proceed()方法)
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    //这个call之前介绍过,在这里获取这个call对象
    Call call = realChain.call();
    //状态监听
    EventListener eventListener = realChain.eventListener();
    //连接池,获取一个正确的连接
    StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
    this.streamAllocation = streamAllocation;

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {  //开始循环
      if (canceled) {  // 判断请求是否取消
        streamAllocation.release();  //连接被释放
        throw new IOException("Canceled");
      }

      Response response;
      boolean releaseConnection = true;
      try {
        //调用下一个拦截器
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        //   上面的翻译(路由连接的尝试失败。请求将不会被发送)
        if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
          throw e.getFirstConnectException();
        }
        releaseConnection = false;
      //出现异常,重试
        continue;  
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        //出现连接关闭异常,尝试恢复
        if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
        releaseConnection = false;
        continue; //继续重试
      } finally {
        // We're throwing an unchecked exception. Release any resources.   //关闭资源
        if (releaseConnection) {
          streamAllocation.streamFailed(null);
          streamAllocation.release();
        }
      }

      // Attach the prior response if it exists. Such responses never have a body.
      //前一个重试获取的response存在
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }

      Request followUp;
      try {
      //封装新的重试Request,为它添加一些内容,比如验证头
        followUp = followUpRequest(response, streamAllocation.route());
      } catch (IOException e) {
        streamAllocation.release();
        throw e;
      }
      
      //请求体为空,返回response。莫非是请求成功,请求体是空的?反正我是这么想的。
      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }

      closeQuietly(response.body());
      //重试次数超过允许最大次数,释放连接,抛出异常
      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      if (followUp.body() instanceof UnrepeatableRequestBody) {
        streamAllocation.release();
        throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
      }

      if (!sameConnection(response, followUp.url())) {
        streamAllocation.release();
        streamAllocation = new StreamAllocation(client.connectionPool(),
            createAddress(followUp.url()), call, eventListener, callStackTrace);
        this.streamAllocation = streamAllocation;
      } else if (streamAllocation.codec() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }

      request = followUp;
      priorResponse = response;
    }
  }
}

这个拦截器,从上往下看下来,可以这么理解,当一个请求,由于各种原因,获取数据失败了,就尝试着去重连恢复。否则就重新封装request(followUp),通过拦截器链继续执行这个请求。源码中有个
if(followUp == null) 就返回response,当followUp == null的时候,这个重试流程就结束了。

很好,接下来,我们来看BridgeInterceptor,先来看下一张图(网络上找的):

《okHttp拦截器分析(一)》 image.png

图中很清晰的说明这个拦截器的作用:给request添加请求头,给resonpse添加响应头。

放码过来吧(关键代码):

public final class BridgeInterceptor implements Interceptor {
  ......
@Override public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();
    
    //给request添加contentType
    RequestBody body = userRequest.body();
    if (body != null) {
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) { //如果body有值,请求头设置body的内容长度,并且移除传输编码
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {  //否则,设置传输编码(这里是分块传输编码),移除内容长度值
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }
    }

    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }

    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive");
    }

    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }

    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添加header
    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();
  }
}

有点意思,专门搞个拦截器,给请求前的request,返回后的response添加header内容。

篇幅过长,下篇继续分析后面的拦截器,预知后事如何,且看下集分解。

    原文作者:你毅哥
    原文地址: https://www.jianshu.com/p/da82149190e1
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞