okhttp的拦截器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/f409031mn/article/details/84780275

okhttp 其实就做了 3 个操作,分别是 请求(call) ,TCP 连接(Connection) ,数据流(okio),这 3 个操作都是通过 okhttp 的拦截器来完成的

okhttp 的拦截器到底是有些,我们可以在 opkhttp3/RealCall 里面的一个方法看到:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    //添加开发者设置的拦截器,也就自定义的拦截器
    interceptors.addAll(client.interceptors());
    //添加失败重试的拦截器
    interceptors.add(retryAndFollowUpInterceptor);
   /**
    * BridgeInterceptor 网络桥接拦截器
    * 这里处理http的报文, Content-Length 的计算和添加、gzip的支持
    * 等都是在这里面处理的
    */
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //添加缓存拦截器
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //添加连接拦截器,在这里建立TCP或SSL连接
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
        //添加webSock长连接的拦截器
        interceptors.addAll(client.networkInterceptors());
    }
    /**
     * CallServerInterceptor 请求服务器响应拦截器,负责网络连接
     * 它负责实质的网络请求与响应的 I/O 操作,即往 Socket 里写入请求数据
     * 和从 Socket 里读取响应数据
     */
    interceptors.add(new CallServerInterceptor(forWebSocket));
    //RealInterceptorChain配置全部的拦截器以及拦截器的调度顺序
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, this, eventListener
, client.connectTimeoutMillis(),client.readTimeoutMillis()
, client.writeTimeoutMillis());
    //启动拦截器去进行网络请求与响应
    return chain.proceed(originalRequest);
}

这个方法就是把所有配置好的 Interceptor 放在一个 List 里,然后作为参数,创建一个 RealInterceptorChain 对象,它负责全部的拦截器的调度,它是通过调用 chain.proceed(request) 方法来切换到另外一个拦截器,这种叫做责任链模式
那么,一个完整的 http 请求过程,这些拦截器的调用如下图:
在这里插入图片描述
首先,先从发起一个 http 请求开始,从上到下,每级 Interceptor 做的事:

  • client.interceptors():这里是开发者设置的自定义 Interceptor
    • 在发起 http 请求报文时,在其他 Interceptor 处理之前,进行最早的预处理工作,比方说可以在这里添加统一的 header;
    • 在接收 http 响应报文时,这里则是最后的善后操作了,比方将服务器返回的响应报文的内容全部打印出来
  • RetryAndFollowUpInterceptor:该拦截器负责在请求失败时的重试,以及重定向的自动后续请求,也就是连接到新的 IP 地址;
  • BridgeInterceptor:该拦截器负责把用户构造的 http 请求转换为 http 请求报文以及得到服务器返回的数据包转为 http 响应报文,例如 Content-Length 的计算和添加,gzip 的支持和 gzip 数据包的解压都是在这里处理的
  • CacheInterceptor :该拦截器负责 Cache 的处理,通过 Expires,ETag,Last-Modified 等字段判断本地是否有了可用的 Cache,如果有那么就直接忽然接下来的网络交互,直接返回 Cache 中的数据,如果没有,那么继续执行网络请求并在得到 http 响应报文时更新本地的 Cache 数据
  • ConnectInterceptor:该拦截器负责建立起 TCP 连接 或 SSL 连接,这里的代码其实非常少:
public final class ConnectInterceptor implements Interceptor {
  public final OkHttpClient client;
  public ConnectInterceptor(OkHttpClient client) {
    this.client = client;
  }
  @Override public Response intercept(@NonNull Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
   //httpCodec http的编码解码器
    HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
    //RealConnection,和服务器搭建起的连接对象
    RealConnection connection = streamAllocation.connection();
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
}       

上面代码的核心就是 HttpCodec 对象,它是一个 http 协议的接口,子类分别为 Http1CodecHttp2Codec ,它们的代码都很长,目前只要知道它们都是通过 okio 来拼接出 http 请求报文发给服务器以及解析服务器返回的 http 响应报文,也就是对 Socket 的读写操作进行封装(关于 okio 我会在晚些的分享中进行分析)

  • CallServerInterceptor:该拦截器负责实质的请求与响应的 I/O 操作,也就是最后一个调用或最开始调用的拦截器了,简单看下它的代码:
Override
public Response intercept(@NonNull Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    HttpCodec httpCodec = realChain.httpStream();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    RealConnection connection = (RealConnection) realChain.connection();
    Request request = realChain.request();

    long sentRequestMillis = System.currentTimeMillis();

    realChain.eventListener().requestHeadersStart(realChain.call());
    //向服务器发送request请求
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
        // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
        // Continue" response before transmitting the request body. If we don't get that, return
        // what we did get (such as a 4xx response) without ever transmitting the request body.
        if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
            httpCodec.flushRequest();
            realChain.eventListener().responseHeadersStart(realChain.call());
            //读取响应报文
            responseBuilder = httpCodec.readResponseHeaders(true);
        }

        if (responseBuilder == null) {
            // Write the request body if the "Expect: 100-continue" expectation was met.
            realChain.eventListener().requestBodyStart(realChain.call());
            long contentLength = request.body().contentLength();
            CountingSink requestBodyOut =new 
                    CountingSink(httpCodec.createRequestBody(request, contentLength));
            BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
            request.body().writeTo(bufferedRequestBody);
            bufferedRequestBody.close();
            //发送request body
            realChain.eventListener()
                    .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
        } else if (!connection.isMultiplexed()) {
            // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
            // from being reused. Otherwise we're still obligated to transmit the request body to
            // leave the connection in a consistent state.
            streamAllocation.noNewStreams();
        }
    }
   //结束发送请求报文
    httpCodec.finishRequest();

    if (responseBuilder == null) {
    realChain.eventListener().responseHeadersStart(realChain.call());
    responseBuilder = httpCodec.readResponseHeaders(false);
    }

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

大致还是可以看出上面到底干了什么,那就是向服务器发送请求报文和解析服务器返回的响应报文,同时也可以看到核心工作都是由 HttpCodec 完成,而 HttpCodec 实际上是 okio 的封装利用,也就是说 okhttp 其实就是对 okio 的进一步封装,让其具备网络请求的能力。
内容参考:https://blog.piasy.com/2016/07/11/Understand-OkHttp/

猜你喜欢

转载自blog.csdn.net/f409031mn/article/details/84780275