拦截器链(Interceptor)应该是okhttp框架的核心之一。
okhttp的请求拦截器的实现是一种典型的责任链模式,责任链模式一般需要拦截类实现同样的接口。在okhttp中有一个Interceptor接口,默认的拦截器和我们自定义的拦截器都要实现这个接口。Interceptor 接口内部还有一个内部接口Chain。在okhttp中一般使用的是他的具体实现类RealInterceptorChain
下面Interceptor接口的部分代码:
public interface Interceptor {
//chain里面包含request等对象,通过chain来处理并传递请求
Response intercept(Chain chain) throws IOException;
interface Chain {
//返回chain所持有的request
Request request();
//把request交给下一个拦截器处理
Response proceed(Request request) throws IOException;
........................
}
}
在上一节讲到的重要方法getResponseWithInterceptorChain()的最后一段,我们构建了一个RealInterceptorChain对象,并调用他的proceed()方法来获取Response。
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
/*
下面是 RealInterceptorChain的构造函数,里面的参数需要在拦截器链中传递
*/
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;//索引,后面会根据索引来选择相应的拦截器
this.request = request;
this.call = call;
this.eventListener = eventListener;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
this.writeTimeout = writeTimeout;
}
这里是第一次调用proceed方法,把原始的请求传进去,并通知下一个拦截器进行处理。如果我们没有添加自定义的拦截器的话,一般来说,处理request的第一个拦截器会是 RetryAndFollowUpInterceptor,主要作用是构建 StreamAllocation以及完成收到response后的请求重试及重定向工作。
下面直接来看RealInterceptorChain的proceed()方法:
//RealInterceptorChain里面的方法
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
//如果索引数大于拦截链的长度,就抛异常
calls++;
// If we already have a stream, confirm that the incoming request will use it.
//确保新进来的请求会使用已有的流
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");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
//只有一个请求可以调用拦截链执行
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
//调用拦截链中的下一个拦截器,会根据下面这个 next 对象的index参数去取相对应的拦截器
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);//注意这个index + 1
Interceptor interceptor = interceptors.get(index);//根据index取拦截器
//如果没有自定义拦截器的话,第一个取到的就是重定向拦截器,之后会调用各个interceptor的intercept方法
//调用拦截器的intercept方法并获取到返回的response
Response response = interceptor.intercept(next);
//下面就是处理返回的response了
// Confirm that the next interceptor made its required call to chain.proceed().
//确认下一个拦截器确实调用了一次 chain.proceed()
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
//确保返回的response不为null
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
//response的body也不能为null
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
//下面是RetryAndFollowUpInterceptor的intercept方法中的部分代码
//可以看到一般在intercept方法中会递归调用proceed方法,把request递给下一个拦截器处
............
Response response;//返回的response
boolean releaseConnection = true;
try {//传入request,并交给
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
}
................
在okhttp在初始化一个 RealInterceptorChain 对象后,可以通过调用proceed()方法,传入 RealInterceptorChain 对象,在proceed()方法内,会通过调用拦截器的Intercept的方法,通知下一个拦截器对RealInterceptorChain 对象的属性进行设置(主要是处理request和response)。
(注:RealInterceptorChain 对象有一个名为index的int型变量,正式通过index这个属性,来正确的选择拦截器。比如:index = 0,就表示选择第一个拦截器)
在拦截器的Intercept又会递归调用proceed()方法,把RealInterceptorChain 对象传给下一个对象进行处理。
Intercept方法的工作可以分为两部分,第一部分是在调用proceed()方法前,这部分的任务主要是向服务器发送request前的准备。第二部分是在在调用proceed()方法后,对proceed()方法返回的response(也就是从下面的拦截器传递上来的服务器返回的response)进行处理。
简略流程: