okhttp3拦截器——基于责任链模式的拦截器的工作流程

前言


之前分析了okhttp3的基本工作流程,其中重点说明了分发器、高并发线程池设计、任务的分发和转换原理,后面还有一个比较重要的5大拦截器还没有具体深入研究,其实okhttp中核心工作基本上都是在拦截器中执行的,接下来这篇文章就带大家分析五大拦截器究竟是怎么协同工作的,每个拦截器究竟干了什么事情。

基于责任链模式的拦截器的工作流程


其实拦截器的工作流程就像是我们去办理业务时的流程,下面举一个简单的例子

新进社畜小王出差回来,带了一堆发票要公司报销,然后就出现了下面的事情

小王进入财务室。。。

小王:钱掌柜,我之前出差,住宿,吃饭,交通都产生了一笔费用,这些是发票,麻烦报销下

小王把一堆发票双手捧住递给了财务钱掌柜。。。

财务:你这不行啊,你得把这个申请单填好,而且你来我这报销必须得要人事部门同意啊,不然怎么知道你是不是真的去出差了

财务打出了一份报销申请单,递给了小王。。。

小王拿着申请单和一堆发票进入了人事部。。。

小王:人事小姐姐你好,这是我的报销申请单,需要你在上面签字,我才能找财务报销

人事:你这不行啊,没你的部门老大签字,鬼知道你去出差干嘛了

小王很烦躁地从人事部走出来,拿着申请单到了技术部经理的办公室。。。

小王:秃经理,我这里有份报销申请单,需要你签字后,人事才能签字,人事签字后,财务才能签字,然后报销才能到账,就问你签不签!

技术部经理:不要激动!你这几天出差帮公司解决了难题,我这不会卡你的

秃经理在报销申请单上签上了自己的名字。。。

人事在报销申请单上签上了自己的名字。。。

财务在报销申请单上签上了自己的名字。。。

小王银行卡到账1000元

上面的例子很简单地展示了一整个的报销流程,大概意思就是每个环节都必须要满足上一个环节,否则就不执行,下面是一个简单的流程图

image-20210427091724045

这个流程图也类比了okhttp的五大拦截器之间的流转。

五大拦截器的流转原理是基于责任链模式的,一个请求过来首先会依次流经每个拦截器,但是每个拦截器都是要求下一个拦截器返回结果后再去往下走,接下来直接看源码

我们直接从RealCallgetResponseWithInterceptorChain方法开始看

final class RealCall implements Call {
    ...
    Response getResponseWithInterceptorChain() throws IOException {
        // Build a full stack of interceptors.
        List interceptors = new ArrayList<>();//代码1
        interceptors.addAll(client.interceptors());
        interceptors.add(new RetryAndFollowUpInterceptor(client));
        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));//代码2

        Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
            originalRequest, this, client.connectTimeoutMillis(),
            client.readTimeoutMillis(), client.writeTimeoutMillis());//代码3

        boolean calledNoMoreExchanges = false;
        try {
          Response response = chain.proceed(originalRequest);//代码4
          if (transmitter.isCanceled()) {
            closeQuietly(response);
            throw new IOException("Canceled");
          }
          return response;
        } catch (IOException e) {
          calledNoMoreExchanges = true;
          throw transmitter.noMoreExchanges(e);
        } finally {
          if (!calledNoMoreExchanges) {
            transmitter.noMoreExchanges(null);
          }
        }
  }
    ...
}

代码流向:代码1->代码2,这段代码执行的过程就是将拦截器加入到列表中的过程,执行完后,会得到这样一个拦截器列表:

拦截器列表:interceptors 释义
client.interceptors 用户自定义的拦截器
RetryAndFollowUpInterceptor 重试和重定向拦截器
BridgeInterceptor 桥接拦截器
CacheInterceptor 缓存拦截器
ConnectInterceptor 连接拦截器
client.networkInterceptors 当网络请求回来后,用户自定义的拦截器
CallServerInterceptor 和服务器通信的拦截器

上面的顺序十分重要,关系到拦截器的流转流程,在这里假设用户没有自定义拦截器列表即client.interceptors.size == 0

代码3中创建了一个chain,翻译过来就是链条,这个chain的左右就是将5大拦截器串联起来

代码4开始就是拦截器流转的开端,接下来我们具体看看,okhttp是如何将从一个拦截器流转到另一个拦截器的

public interface Interceptor {
    ...
    interface Chain {
        ...
        Response proceed(Request request) throws IOException;//代码5
        ...
    }
    ...
}
public final class RealInterceptorChain implements Interceptor.Chain {
    ...
    @Override public Response proceed(Request request) throws IOException {
        return proceed(request, transmitter, exchange);//代码6
    }
    public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
      throws IOException {
        ...
        RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
            index + 1, request, call, connectTimeout, readTimeout, writeTimeout);//代码7
        Interceptor interceptor = interceptors.get(index);//代码8
        Response response = interceptor.intercept(next);//代码9
        ...
        return response;
  }
}

Chain这个接口的唯一实现只有RealInterceptorChain所以这里的代码流向应该是:代码4->代码5->代码6->代码7

先看下代码7这里生成了一个Chain的实例,其中第一个参数interceptors不变,还是原来的拦截器列表,变化的是index,这个成员变量是用来指定当前链条指向哪个拦截器,所以可以推测代码7实际上就是生成指向下一个拦截器的链条。走到代码8可以发现取出的是当前的(第index个)拦截器,而代码9则是将下一个拦截器的链条做为参数传递到了拦截器的intercept()方法中执行。继续跟代码,看看interceptor.intercept(next)这里做了什么。在这之前记住当前index == client.interceptors.size而我们之前假设用户没有自定义拦截器所以index == 0,所以代码8取出的拦截器是RetryAndFollowUpInterceptor。

public interface Interceptor {
    Response intercept(Chain chain) throws IOException;//代码10
    ...
}
public final class RetryAndFollowUpInterceptor implements Interceptor {
    @Override public Response intercept(Chain chain) throws IOException {
        ...
        RealInterceptorChain realChain = (RealInterceptorChain) chain;//代码11
        ...
        while (true) {
         ...
          Response response;
          ...
          try {
            response = realChain.proceed(request, transmitter, null);//代码12
            success = true;
          } catch (RouteException e) {
         ...
          }
          ...
  }
}

代码流向:代码9->代码10->代码11

可以很清楚地知道代码11中获得的realChain实际上就是代码9中传递过来的下一个链表。我们再看看代码12

image-20210427100847912

好熟悉,这不就又回到了代码5->代码6->代码7这个代码流向了吗?,但是不同的是之前就已经在代码9中将index+1,所以当前index == 1,所以代码8取到的拦截器应该是BridgeInterceptor,简单看一下BridgeInterceptor

public final class BridgeInterceptor implements Interceptor {
    ...
    @Override public Response intercept(Chain chain) throws IOException {
        ...
        Response networkResponse = chain.proceed(requestBuilder.build());//代码13     
        ...
     }
    ...
}

果然BridgeInterceptor也会通过链表chain来执行下一个拦截器,这样就形成了一个驱动型的链表,上一个链表会驱动下一个链表去执行拦截器中的intercept方法,而intercept()方法又会驱动下一个链表...由此拦截器列表就能执行起来了。

值得注意的是Interceptor接口中有个返回值Response,为啥要有这个?

还记得开篇讲过社畜小王报销流程吗,他先找财务财务要求人事签名才继续下面的工作,不然不给钱,这个签名就类比Response,在okhttp拦截器中的含义就是,我RetryAndFollowUpInterceptor拦截器得先有一个Response才能执行下面的操作,而这个Response得由下一个拦截器(BridgeInterceptor)给我,下一个拦截器也要一个Response,而这个拦截器得由下下一个(CacheInterceptor)给...所以一旦执行了只有当没有下一个拦截器时,拦截器才会终止往下传,一个个拦截器收到了Response后才会继续下面的工作。


到这里流程基本上就通了,接下来会围绕上面的列表,详细讲讲5大拦截器里每个拦截器做了哪些事情。

你可能感兴趣的:(okhttp3拦截器——基于责任链模式的拦截器的工作流程)