OkHttp发起请求源码阅读--拦截器(二)

212 阅读3分钟

1.getResponseWithInterceptorChain方法

OkHttp发起请求源码阅读(一)中最后提到,RealCall的execute方法中通过getResponseWithInterceptorChain方法拿到了Response

  @Override 
  public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }

getResponseWithInterceptorChain()方法中如果抛出异常会直接被catch到,回调callFailed方法

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);
  }

getResponseWithInterceptorChain()方法中主要是添加了几个拦截器

2.拦截器功能

  1. RetryAndFollowUpInterceptor 处理重试的一个拦截器,会去处理一些异常,只要不是致命的异常就会重新发起一次请求(把Request给下级),如果是致命的异常就会抛给上一级; 会处理一些重定向等等,比如 3XX 307、407 就会从头部中获取新的路径,生成一个新的请求交给下一级(重新发起一次请求)

  2. BridgeInterceptor 做一个简单的处理,设置一些通用的请求头,Content-Type Connection Content-Length Cookie 做一些返回的处理,如果返回的数据被压缩了采用 ZipSource , 保存 Cookie

  3. CacheInterceptor 在缓存可用的情况下,读取本地的缓存的数据,如果没有直接去服务器,如果有首先判断有没有缓存策略,然后判断有没有过期,如果没有过期直接拿缓存,如果过期了需要添加一些之前头部信息如:If-Modified-Since ,这个时候后台有可能会给你返回 304 代表你还是可以拿本地缓存,每次读取到新的响应后做一次缓存。

  4. ConnectInterceptor findHealthyConnection() 找一个连接,首先判断有没有健康的,没有就创建(建立Scoket,握手连接),连接缓存得到一条结论:OkHttp 是基于原生的 Socket + okio(原生IO的封装) 封装 HttpCodec 里面封装了 okio 的 Source(输入) 和 Sink (输出),我们通过 HttpCodec 就可以操作 Socket的输入输出,我们就可以像服务器写数据和读取返回数据

  5. CallServerInterceptor 写数据和读取数据 写头部信息,写body表单信息等等

3.拦截器工作流程

拦截器的工作流程类似于安卓中事件分发机制,参考责任链模式

  • 链的主要结构
  • 链的procced方法
  • 节点的intercept方法
  • 从上层往下层分发,发到最底层后再往上传递
  • 上层也可以直接将事件拦截直接返回
  • 每个拦截器中intercept方法如果返回realChain.proceed(request, streamAllocation, httpCodec, connection);就将request传递给下个拦截器
  @Override 
  public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    ......
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
  • 每个拦截器中intercept方法如果返回response,就将response返回上一级,直到返回最上级后就会通过回调将response传递给我们
  @Override 
  public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    ......
    Response response = responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();
    ......
    return response;
  }

就用其中三个拦截器举例

  1. 所有拦截器都正常工作情况
  2. 第二个拦截器直接将response返回情况

我们也可以添加自己的拦截器,加上我们需要的功能