OkHttp源码相关(二)-拦截器

主要流程篇 OkHttp源码相关(一) 。
拦截器篇 OkHttp源码相关(二) 。

1.责任链模式

说到拦截器就不得不提一下他的设计模式:责任链模式为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。在责任链模式中,每一个对象对其下家的引用而接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的 情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。
看文字有点蒙蔽,我们看下图, 如果我发起一个请求request,那么这个责任链模式下就会一层层的去调用处理我这个request,当最后一层处理完毕以后又会一层层的返回response返回值,最后返回到发起者这里,这就是责任链模式。当然也有可能我发起的request并没有返回的response,而是在某一层就被符合条件的interceptor给处理掉了,也是符合责任链模式的。


image.png

2.五大拦截器

拦截器就是用到了刚才说的责任链模式


image.png
  1. RetryAndFollowUpInterceptor
    重试拦截器在交出(交给下一个拦截器)之前,负责判断用户是否取消了请求;在获得了结果之后,会根据响应码判断是否需要重定向,如果满足条件那么就会重启执行所有拦截器。第一个接触到请求,最后接触到响应;负责判断是否需要重新发起整个请求。
  2. BridgeInterceptor
    桥接拦截器在交出之前,负责将HTTP协议必备的请求头加入其中(如:Host,Content-Type,Keep-Alive等,其实就是方便用户,省了用户自己加了)并添加一些默认的行为(如:GZIP压缩);在获得了结果后,调用保存cookie接口并解析GZIP数据。 补全请求,并对响应进行额外处理。
  3. CacheInterceptor
    缓存拦截器交出之前读取并判断是否使用缓存;获得结果后判断是否缓存。请求前查询缓存,获得响应并判断是否需要缓存,主要是看networkRequest与cacheResponse着两个
@Override public Response intercept(Chain chain) throws IOException {
    //1 尝试通过url的md5数据从文件缓存查找(get方法)
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;
    //2 如果不允许使用网络并且缓存为空,Resposne返回504,啥也不让搞就gg了
    if (networkRequest == null && cacheResponse == null) {
      return new Response;
    }
    //3 如果不允许使用网络,但是有缓存,返回缓存。
    if (networkRequest == null) {
      return cacheResponse;
    }
    //4 调用下一个拦截器
      networkResponse = chain.proceed(networkRequest);
    //5 如果缓存不为空,但是网络请求得来的返回码是304(无修改的),则更新缓存的响应
    if (cacheResponse != null) {
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        Response response = cacheResponse.newBuilder()
        return response;
      } 
    }
    //6 使用网络请求得到的Resposne,并且将这个Resposne缓存起来
    Response response = networkResponse;
    cache.put(response);

    return response;
  }

networkRequest存在则优先发起网络请求,否则使用cacheResponse缓存,若都不存在则请求失败。


image.png
  1. ConnectInterceptor
    连接拦截器在交出之前,负责找到或者新建一个连接,并获得对应的socket流;在获得结果后不进行额外的处理。与服务器完成TCP连接。主要是负责连接池socket连接的复用(判断连接是否可用,不可用则从ConnectionPool获取连接,ConnectionPool无连接,创建新连接,握手,放入ConnectionPool,最多缓存5个,每个5分钟,超时无使用释放)、代理连接相关,
  2. CallServerInterceptor
    请求服务器拦截器进行真正的与服务器的通信,向服务器发送数据,解析读取的响应数据。
    与服务器通信;封装请求数据与解析响应数据(如:HTTP报文)

在 Response response = getResponseWithInterceptorChain();这个方法里调用了这些拦截器

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

具体看一下拦截器是如何用责任链模式实现的
首先是在一个集合里add进去了这些拦截器,然后把集合放进RealInterceptorChain方法里,并调用proceed方法。注意这里RealInterceptorChain的参数0,然后看下procee的方法,这里对我们之前传过来的0会开始累积了

 public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
     .....
  // 创建新的拦截链,链中的拦截器集合index+1
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    //执行当前的拦截器 默认是:retryAndFollowUpInterceptor
    Interceptor interceptor = interceptors.get(index);
   //执行当前拦截器intercept方法
    Response response = interceptor.intercept(next);
    ......  
    return response;
  }

创建RealInterceptorChain并且下标为index+1(getResponseWithInterceptorChain()中才创建了一个),index是上一个RealInterceptorChain通过构造器传递下来的。这里主要是看一下Interceptor这个接口,每个拦截器都继承了Interceptor接口,在RealInterceptorChain方法里开始调用。

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;
  interface Chain {
    Request request();
    Response proceed(Request request) throws IOException;
    Connection connection();
  }
}

我们看第一个retryAndFollowUpInterceptor方法里,执行到一半,又去执行了RealInterceptorChain中的proceed方法,这就是递归调用了,RealInterceptorChain这个方法会被每个拦截器调用,然后里边的index+1去执行下一个拦截器。

@Override public Response intercept(Chain chain) throws IOException {

    Request request = chain.request();
    // 首先拿到当前真实的Interceptor 实现类
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    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 {
        //又执行了RealInterceptorChain中的proceed方法
        //实际上就是下一个拦截器
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        .....
      }
   }

当最后一个拦截器调用完成后,就会返回Response结果了。
总的来说就是先创建拦截器集合,然后将集合放拦截器链中执行它的processd()方法,在processd()中通过下标获得此次intercetpor,调用intercepter(Chain chain)方法并再次执行下一级拦截器链的processd()方法形成链式调用,当最后一个返回Response之后,会按传过来的顺序再把Response逐渐返回去,最终获得一个完整的调用。


image.png

你可能感兴趣的:(OkHttp源码相关(二)-拦截器)