OKHttp3--拦截器链RealInterceptorChain源码解析【五】

系列

OKHttp3–详细使用及源码分析系列之初步介绍【一】
OKHttp3–流程分析 核心类介绍 同步异步请求源码分析【二】
OKHttp3–Dispatcher分发器源码解析【三】
OKHttp3–调用对象RealCall源码解析【四】
OKHttp3–拦截器链RealInterceptorChain源码解析【五】
OKHttp3–重试及重定向拦截器RetryAndFollowUpInterceptor源码解析【六】
OKHttp3–桥接拦截器BridgeInterceptor源码解析及相关http请求头字段解析【七】
OKHttp3–缓存拦截器CacheInterceptor源码解析【八】
OKHttp3-- HTTP缓存机制解析 缓存处理类Cache和缓存策略类CacheStrategy源码分析 【九】

Interceptor

前面解析了OKHttp核心之一的分发器Dispatcher,这篇文章来解析另一个核心知识点拦截器Interceptor

何为拦截器呢?

其实听这个名字我们也大概直到拦截器的意思,在OKHttp中,我们发出的HTTP请求并不是直接就连接到服务器然后获取结果,而是由OKHttp中的拦截器截获我们发出的请求,它可以观察,修改并可能使请求中断,然后返回结果,通常情况下,拦截器会对request或者response的头部headers进行添加,删除,转换操作

总而言之,拦截器是OKHttp提供的一种强大机制,它可以实现网络监听,请求以及响应重写,失败重连等功能

在OKHttp中,Interceptor是一个接口,定义了一个方法,以实现拦截功能

Response intercept(Chain chain) throws IOException;

其中入参是其内部定义的一个接口,具体实现类是RealInterceptorChain

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    Connection connection();
  }

其中proceed方法很重要,参数是我们的请求对象;当拦截器链开始工作时,取出第一个拦截器,并实例化一个新的拦截器链,传入参数并执行它们的intercept方法;如果拦截器需要下一个拦截器获取响应,那就通过参数拿到拦截器链再次执行proceed方法,这里取出第二个拦截器,继续调用该拦截器的intercept方法;并依次类推,直到获取响应

RealInterceptorChain

还记得前面的文章里讲到同步异步请求的时候,最终都会走到RealCall中的getResponseWithInterceptorChain方法

RealCall.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);
    return chain.proceed(originalRequest);
  }

在实例化RealInterceptorChain之前,需要实例化很多有着具体功能的拦截器,它们分为三类,如图

在这里插入图片描述

  • 蓝色块上方是APPLication Interceptor,也就是应用程序拦截器,即开发者自己自定义的拦截器,代码中的client.interceptors()获取的就是这类拦截器
  • 蓝色块下方是NetWork Interceptor,也就是网络拦截器,用来观察单个网络请求和响应,只能调用一次proceed方法
  • 蓝色块代表的就是OKHttp提供的拦截器,共有5个,也是我们需要关注的重点

5个拦截器

  • RetryAndFollowUpInterceptor:Interceptor实现类之一,是网络请求失败时进行重连及服务器返回当前请求需要进行重定向的拦截器
  • BridgeInterceptor:桥接连接器,主要是进行请求前的一些操作,将我们的请求设置成服务器能识别的请求,比如设置一系列头部信息,比如设置请求内容长度,编码,gzip压缩,cookie等
  • CacheInterceptor:缓存拦截器,这个就很明显了,作用是缓存请求和响应,比如同一个请求之前发生过,这次就不需要重新构建了,直接从缓存取;响应同理
  • ConnectInterceptor:连接拦截器,为当前请求找到一个合适的连接,比如如果从连接处中可以找到能复用的连接,就不要再创建新的连接了
  • CallServerInterceptor:连接服务器拦截器,负责向服务器发送真正的请求,接受服务器的响应

再次回到getResponseWithInterceptorChain方法,当拦截器列表组装完成,就会实例化拦截器对象RealInterceptorChain

Interceptor.Chain chain = new RealInterceptorChain( interceptors, null, null, null, 0, originalRequest);

接收了三个有效参数,然后调用该对象的proceed方法,拦截器之所以可以依次调用,并最终再从后往前返回Response,都依赖于该方法,在解析该方法前,我们看下它的构造方法

构造方法

  //拦截器列表,装载了所有拦截器
  private final List<Interceptor> interceptors;
  //下面三个很重要,后续单独出文章解析
  private final StreamAllocation streamAllocation;
  private final HttpCodec httpCodec;
  private final RealConnection connection;
  //拦截器列表索引
  private final int index;
  //封装请求信息的对象
  private final Request request;
  //proceed方法执行次数
  private int calls;

  public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, RealConnection connection, int index, Request request) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
  }

从前面的实例化可以知道interceptors和request是有值的,同时index是0,其它参数是null,接下来看proceed方法

RealInterceptorChain.proceed


  @Override 
  public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }
 
  /**
  * 依次取出拦截器链表中的每个拦截器去获取Response
  */
  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    //如果索引值大于等于拦截器列表大小,就抛出异常,因为后续会出现数组越界的异常
    if (index >= interceptors.size()) throw new AssertionError();
    
    // 记录本方法调用次数
    calls++;

    // 如果已经为该Request创建了stream,就不再继续创建了
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // 如果已经为该Request创建了stream,那该方法只能调用一次
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // 创建新的拦截器链对象,并将index索引+1
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    //获取拦截器
    Interceptor interceptor = interceptors.get(index);
    //执行拦截器的intercept方法获取结果,并将新的拦截器链对象传入
    Response response = interceptor.intercept(next);

    // 确保该方法只能调用一次
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    return response;
  }

拦截器链的proceed方法并不复杂,其核心逻辑就是三行代码

  • 实例化拦截器链RealInterceptorChain ,但是要注意到其中一个参数index是+1了,不像RealCall中是0;因为只有这样做,下一个拦截器执行拦截器链的proceed方法时,才能根据索引取出下一个拦截器,不至于总是把自己取出来;这样就形成了拦截器链的概念
  • 根据index取出拦截器,因为index每次都+1,所以就能根据拦截器列表添加的顺序取出拦截器,依次取出RetryAndFollowUpInterceptor,BridgeInterceptor,CacheInterceptor,ConnectInterceptor,CallServerInterceptor(开发者未添加拦截器的情况下)
  • 执行拦截器的intercept方法,并把实例化的RealInterceptorChain传入,这样每个拦截器在intercept方法又能调用RealInterceptorChain的proceed方法,这样循环

总结

这也就是拦截器链的执行逻辑了,到这里我们也可以简单理解OKHttp中的一个网络请求其实就是5个拦截器依次执行自己的intercept方法的过程

  • 组装我们的请求对象Request
  • 调用下一个拦截器,获取Response,以此形成拦截器链
  • 对Response进行处理,返回给上一个拦截器
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页