Okhttp之BridgeInterceptor拦截器解析

作者: OneXzgj | 来源:发表于2018-12-20 16:38 被阅读13次

    如果研究过okhttp源码,应该知道okhttp的核心是拦截器,而拦截器所采用的设计模式是责任链设计,即每个拦截器只处理与自己相关的业务逻辑。

    之前已经分析了Okhttp的RetryAndFollowUpInterceptor的原理

    拦截器的核心代码都在intercept(Chain chain )方法中,所以有必要彻底研究该方法是如何处理即可理解BridgeInterceptor的作用和实现。

    1、okhttp中RealCall中请求网络及添加拦截器的核心代码

    如下是okhttp中RealCall.java文件中的将一个request请求,得到response的核心代码,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);
      }
    

    2、 开始分析之前,先来了解一下正常的一次网络请求过程

    一次Http请求必须要携带一些特定的Header信息,例如:以请求百度主页为例,Request中携带的信息如下:


    request.png

    额,貌似有点看不清,看下图,是不是高清无码


    request头信息.png
    标红的这些字段,即Header信息必要字段,水了这么多,就是为了说明白,一次http网络请求并不是只需要设置一个地址就可以,而是要封装一个特定的格式和必要的参数(即Header信息)。

    3、BridgeInterceptor的核心伪代码

    有了上面的这思想,看BridgeInterceptor的核心伪代码,是不是恍然大悟了!(具体方法作用都表明了注释)

    public final class BridgeInterceptor implements Interceptor {
      private final CookieJar cookieJar;
    
      public BridgeInterceptor(CookieJar cookieJar) {
        this.cookieJar = cookieJar;
      }
    
      @Override 
        public Response intercept(Chain chain) throws IOException {
        Request userRequest = chain.request();
        Request.Builder requestBuilder = userRequest.newBuilder();
    
        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) {
            //添加Content-Length头信息
            requestBuilder.header("Content-Length", Long.toString(contentLength));
            requestBuilder.removeHeader("Transfer-Encoding");
          } else {
            requestBuilder.header("Transfer-Encoding", "chunked");
            requestBuilder.removeHeader("Content-Length");
          }
        }
        //添加Host信息
        if (userRequest.header("Host") == null) {
          requestBuilder.header("Host", hostHeader(userRequest.url(), false));
        }
    
        //添加Connection信息
        if (userRequest.header("Connection") == null) {
          requestBuilder.header("Connection", "Keep-Alive");
        }
    
        //添加 Accept-Encoding信息
        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());
        //添加Cookie头信息
        if (!cookies.isEmpty()) {
          requestBuilder.header("Cookie", cookieHeader(cookies));
        }
    
         //添加User-Agent信息
        if (userRequest.header("User-Agent") == null) {
          requestBuilder.header("User-Agent", Version.userAgent());
        }
    
        Response networkResponse = chain.proceed(requestBuilder.build());
    
        HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
    
        Response.Builder responseBuilder = networkResponse.newBuilder()
            .request(userRequest);
        //如果服务器返回的response是压缩过的,即获取GzipSource输入流,读取返回的数据
        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();
      }
    
    //返回并封装为key=value 形式的cookie信息
      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.1 方法注释已添加到method上

    即获取到用户创建request对象,然后在该基础上添加http请求必要的头信息。如果返回的response信息是压缩后的,在通过okio压缩输入流处理response。

    3.2 再来看看构造方法中传入的CookieJar

    public BridgeInterceptor(CookieJar cookieJar) 
    

    构造中穿传入的CookieJar,会通过cookieHeader()方法,封装为key=value的形式,添加到Header信息中的cookie字段中。

    4、总结一下BridgeInterceptor的作用:

    4.1 封装request头信息
    4.2 处理cookie信息
    4.3 处理服务器压缩后的response

    相关文章

      网友评论

        本文标题:Okhttp之BridgeInterceptor拦截器解析

        本文链接:https://www.haomeiwen.com/subject/ftbykqtx.html