OkHttp3.0(四)-Interceptor拦截器(1)-拦截器链Chain

1.引言 我们在前面几个章节的学习过程中,已经可以明白OkHttp的基本使用,OkHttp在使用的过程中几个常用的类的分析,OkHttp的分配器Dispatcher。其中在RealCall中,不管是同步请求还是异步请求,最终都会调用的一个方法,得到网络请求响应结果Response,就是getResponseWithInterceptorChain()方法。因为这个方法用的是拦截器链调用拦截器执行对应的方法,所以我们决定放到现在来讲,我们先看一下getResponseWithInterceptorChain()的调用位置:

//同步请求获取请求结果 @Override public Response execute() throws IOException { ... try { ... Response result = getResponseWithInterceptorChain(); //获取请求结果 ... return result; } catch (IOException e) { ... } finally { ... } }

上面我们看到的,是我们调用Call的同步请求方法execute()时,的部分代码,可以看到,在该方法中真正获取请求结果的只有一句代码Response result = getResponseWithInterceptorChain(),就是说,同步请求会直接调用getResponseWithInterceptorChain()方法,获取Response并返回个调用者。
我们再看异步请求是如何调用的getResponseWithInterceptorChain()方法,首先我们异步请求调用的是Call的enqueue(Callback)方法:
//Call的异步请求方法,请求结果会利用Callback接口回调返回给调用者 @Override public void enqueue(Callback responseCallback) { ... client.dispatcher().enqueue(new AsyncCall(responseCallback)); }

通过部分代码,我们看到,Call的enqueue(Callback)方法,会调用Dispatcher的enqueue(AsyncCall)方法,我们继续看一下Dispatcher的enqueue(AsyncCall)方法:
//Dispatcher的异步请求方法,被Call调用。Asyncall是异步请求任务 synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); //线程池执行异步任务,最终会执行AsyncCall的execute()方法 } else { readyAsyncCalls.add(call); } }

上面的代码我们前面已经讲过多次,executorService().execute(call)使用线程池异步执行了异步请求任务,其中的请求任务代码,在AsyncCall类的execute()方法中,相当于在异步线程中执行了AsyncCall类的execute()方法,所以我们在跳转到AsyncCall类中,查看一下它的execute()方法:
//RealCall的内部类AsyncCall,用来做异步请求任务,是Runnable的子类。 @Override protected void execute() { ... try { //获取请求结果 Response response = getResponseWithInterceptorChain(); if (...) { ...//判断,使用Callback接口回调返回给调用者 responseCallback.onFailure(RealCall.this, new IOException("Canceled")); } else { ...//判断,使用Callback接口回调返回给调用者 responseCallback.onResponse(RealCall.this, response); } } catch (IOException e) { if (signalledCallback) { ... } else { ...//判断,使用Callback接口回调返回给调用者 responseCallback.onFailure(RealCall.this, e); } } finally { ... } }

上面代码,相信大家都能看懂,在AsyncCall的execute()方法中调用了getResponseWithInterceptorChain()方法。获取到了请求结果,然后经过判断,在利用调用者传进来的Callback接口,做接口回调,将结果回传给调用者,完成了异步请求。
2.getResponseWithInterceptorChain()方法 前面说了这么多,就是为了说明不管是同步请求还是异步请求,都会调用RealCall中的getResponseWithInterceptorChain()来获取请求结果Response,所以我们现在将getResponseWithInterceptorChain()代码放上来,分析一下:
//RealCall的同步、异步请求都会调用此方法,这是获取网络请求结果Response的关键方法 Response getResponseWithInterceptorChain() throws IOException { //创建一个存储拦截器的集合 List interceptors = new ArrayList<>(); //将用户传入的Application拦截器添加到集合中 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) {//如果不是WebSocket连接 //将用户传入的Network拦截器加入的 interceptors.addAll(client.networkInterceptors()); } //将CallServer拦截器(与服务器进行数据交互)加入到集合中 interceptors.add(new CallServerInterceptor(forWebSocket)); //创建拦截器链,index初始值为0 Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); //调用拦截器链的proceed方法,就会调用到所有拦截器,并返回请求结果Response return chain.proceed(originalRequest); }

这一节,我们先不讲代码中提到的每个拦截器的作用,我们只讲拦截器的调用。我们从上面代码中可以看到,首先会将各种拦截器按照先后顺序,分别加入到新创建的ArryList集合中,然后创建了Interceptor.Chain拦截器链实例,在创建过程中,构造函数传入了存有各种拦截器的集合interceptors、数字0、用户创建好的Request对象、当前RealCall对象、OkHttp网络请求详细事件回调接口、超时设置。最后,调用了拦截器链chain的proceed(Request)方法,该方法返回Response正是调用者需要的服务器端的响应结果。我们先大概知道一下,这个方法中,主要做了些什么工作。接下来我们来看拦截器链。
3.Interceptor.Chain,拦截器链 首先我们通过代码查找,看到Interceptor.Chain是一个interface,它的目前唯一实现类就是RealInterceptorChain,我们看下这个类的构造方法
//RealInterceptorChain构造方法 public RealInterceptorChain(List interceptors, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call, EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) { this.interceptors = interceptors; //RealCall那边创建好并且已经添加好了所有拦截器 this.connection = connection; //null this.streamAllocation = streamAllocation; //null this.httpCodec = httpCodec; //null this.index = index; //RealCall传入的是0 this.request = request; //用户构建好的Request this.call = call; //当前RealCall this.eventListener = eventListener; this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; this.writeTimeout = writeTimeout; }

我们需要注意一点,他的变量index正是我们刚才RealCall的getResponseWithInterceptorChain()方法中传入的0,我们接着看一下我们前面调用的RealInterceptorChain的proceed(Request)方法:
//首次调用拦截器链 @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 { ... //这里需要注意,创建的拦截器链RealInterceptorChain,传入的参数中(index+1) RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout); //这里需要注意,获取到的拦截器是角标为index的拦截器,而index正是通过其构造函数赋值的 Interceptor interceptor = interceptors.get(index); //调用拦截器的intercept()方法获取Response Response response = interceptor.intercept(next); ... return response; //将请求结果返回 }

讲到这里,可能很多人还是有些懵懵懂懂,我来帮大家捋一下:
【OkHttp3.0(四)-Interceptor拦截器(1)-拦截器链Chain】1、在RealCall的getResponseWithInterceptorChain()方法中,我们第一次创建了拦截器链对象RealInterceptorChain,将存有拦截器的集合interceptors传给了RealInterceptorChain,同时设置了它的index为0,紧接着调用了RealInterceptorChain的proceed方法;
2、在RealInterceptorChain的proceed方法中,再次创建了RealInterceptorChain的实例,继续将存有拦截器的集合interceptors传递给新创建的RealInterceptorChain,这时给他设置的index为当前index+1,然后从interceptors中获取角标为当前index的Interceptor,执行Interceptor的intercept(Chain)方法,参数Chain就是新创建的index+1的RealInterceptorChain对象,并将获取到的Response返回给上一个调用者;
3、Interceptor本身是interface,其intercept(Chain)方法具体看它的实现类,可以肯定的一点是,intercept(Chain)方法中,除了做拦截器本身该做的一些操作外,会继续调用Chain的proceed方法,进入Chain的proceed方法中继续创建index+1的Chain,并且获取角标为index的Interceptor,调用它的intercept(Chain)方法......一直调用下去,直到最后一个拦截器为止。
上面我说捋一下,我想还有人不太明白,因为到现在为止,还缺少一部分代码,那就是Interceptor的intercept(Chain)方法究竟是怎么样子的,我们先看下Interceptor接口的类继承情况
OkHttp3.0(四)-Interceptor拦截器(1)-拦截器链Chain
文章图片

其中LoggingInterceptor是我自己创建的继承自Interceptor,其他五个都是OkHttp框架已经提供的拦截器,我们在刚才讲getResponseWithInterceptorChain()方法时候,都见过的,先不纠结每一个拦截器的作用。我们主要是为了研究一下这些拦截器的intercept(Chain)方法。getResponseWithInterceptorChain()方法创建了存储拦截器的ArrayList集合,并且将拦截器都存入了该集合,我们刚才提到,会根据顺序,每次index+1来获取interceptors中的Interceptor,并调用其intercept(Chain)方法。我们取两个具有代表性的进行研究,这五个拦截器中,按照添加顺序排在最前面的RetryAndFollowUpInterceptor和排在最后的CallServerInterceptor,分别看一下他们的intercept(Chain)方法
RetryAndFollowUpInterceptor的intercept(Chain)方法
@Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); RealInterceptorChain realChain = (RealInterceptorChain) chain; ... StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()), call, eventListener, callStackTrace); ... while (true) { ...//此处调用了拦截器链RealInterceptorChain的proceed方法 response = realChain.proceed(request, streamAllocation, null, null); ... return response; ... } }

我们可以发现,在RetryAndFollowUpInterceptor的intercept(Chain)方法中,的确调用了拦截器链RealInterceptorChain的proceed()方法,这样就可以继续往下,调用下一个Interceptor的intercept(Chain)方法,一直调用下去,直到最后一个拦截器CallServerInterceptor结束。
CallServerInterceptor的intercept(Chain)
//这是CallServerInterceptor的intercept(Chain)方法,该方法不会调用Chain的proceed方法 @Override public Response intercept(Chain chain) throws IOException { ... Response response = responseBuilder .request(request) .handshake(streamAllocation.connection().handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); ... return response; }

通过仔细阅读该代码,发现CallServerInterceptor果然没有继续调用Chain的proceed方法。拦截器链Chain的proceed方法,第一次调用,是被RealCall类的getResponseWithInterceptorChain()方法,然后proceed获取角标为index的Interceptor,调用Interceptor的intercept(Chain)方法,index+1,intercept(Chain)方法继续调用下一个RealInterceptorChain的proceed方法,一直这样调用下去,直到最后一个拦截器,这就是拦截器链的调用。
4.总结 我们先通过两幅幅图来描述拦截器链的工作
OkHttp3.0(四)-Interceptor拦截器(1)-拦截器链Chain
文章图片

OkHttp3.0(四)-Interceptor拦截器(1)-拦截器链Chain
文章图片

我们来简单的总结一下本章要跟大家分享的:
1、OkHttp的网络请求,其本质上是由拦截器链的调用完成的;
2、拦截器链的调用,起始于RealCall的getResponseWithInterceptorChain方法,结束与最后一个拦截器CallServerInterceptor的intercept(Chain)方法;
3、连接器链的调用,RealInterceptorChain的作用是按照顺序依次调用每一个拦截器的intercept(Chain)方法。拦截器Interceptor的作用是发挥自身拦截器的特性,对Request和Response做出操作,调用RealInterceptorChain的proceed()方法,让其继续下去;
4、我们可以将拦截器的调用看作递归,逐层向下调用,直到最后一个拦截器返回Response,然后在逐层向上,一边处理Response一边返回给上一层,最终返回给调用。
本章节关于拦截器链的讲解到此结束了,拦截器是OkHttp框架的精髓,所以我们会拿出几篇文章来详细分析,下一章节开始我们将分析每一个拦截器。




    推荐阅读