之前第二部分的时候留下一块东西没有解决,就是Interceptor部分,这两天大概看了一下,做一下大致的整理吧。
OkHttp框架里面对应用层相关的协议封装的真的是很好,以至于我看着看着发现看到什么代理啊路由啊TLS信息啊什么的,感觉这些实在是深入不下去了。如果以后真的要深入用到OkHttp且需要关系到这些的时候再来看好了。
Interceptor
中文解释是拦截器,它所实现的功能,就是对request和response拦截,进行相应的修改和处理之后得到我们更想要得到的结果。例如之前(二)里面看到的retryAndFollowUpInterceptor,就是一个重定向拦截器,当得到一个网络响应之后,通过判断responseCode来决定是否需要重定向到新的URL,自动发送新的HTTP请求,这样就免去了我们自己去进行这一部分的操作。另外,如果我们要增加缓存操作,也可以通过Interceptor来实现,如果缓存中存有对应请求的内容,就直接返回缓存中的response,反之在进行网络请求。我们也可以自定义Interceptor来实现自己想要的操作。不得不说这个设计真的很强。
流程图
首先先看一下上一篇提到过一个RealCall中用于网络请求的源码:
private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
这部分的chain.proceed()处理流程大致如下,箭头表示整个数据处理的走向:
在我看来有点像是一个栈,在处理request的时候interceptor依次入栈,然后出处理response的时候再依次出栈。接下来再看看一下里面是怎么处理的。
Interceptor接口
interceptor的作用,源码中是这么解释的(以下是Google翻译):观察,修改并潜在地缩短请求发出的请求并返回相应的响应。通常拦截器会在请求或响应中添加,删除或转换标头。
源码如下:
public interface Interceptor {
//用于对request进行相应的处理,将处理后的request交给chain.proceed进行继续的网络请求
//对chain.proceed返回的Response,进行相应的处理,将处理之后的Response返回
Response intercept(Chain chain) throws IOException;
//拦截链,一般Chain实例中会有一个list用于存储所有的interceptor,然后按照list的顺序访问每个interceptor
interface Chain {
//返回request
Request request();
//proceed会将request顺序地传递给每个拦截器进行相应的处理,
//然后将response结果逆序的传递给每个拦截器进行相应的处理
Response proceed(Request request) throws IOException;
Connection connection();
}
}
这里面Chain就是用于这种链式(我个人觉得像是栈式)结构的interceptor的实现。在OkHttp中,其实现类为RealInterceptorChain。
RealInterceptorChain
在RealInterceptorChain中,定义了一个list<Interceptor>用于存储所有的拦截器,然后在proceed方法中进行相应的调用。重点看一下proceed方法:
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpStream, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
Connection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
//初始值为0,每次被调用会++
calls++;
// 用于保证httpStream会被使用,且拦截器必须保持host和port不变
if (this.httpStream != null && !sameConnection(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// 保证每个interceptor只会调用一次proceed方法
if (this.httpStream != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// 新实例化一个chain,并将index指向下一个interceptor
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpStream, connection, index + 1, request);
//获取当前的interceptor
Interceptor interceptor = interceptors.get(index);
//调用interceptor的interceptor方法
Response response = interceptor.intercept(next);
// 保证下一个interceptor会调用且只调用一次proceed()方法
if (httpStream != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// 保证response不为空
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
整个代码也不多,主要是获取到List中当前位置的interceptor,然后新建一个RealInterceptorChain实例next,将index指向下一个interceptor,再调用当前Interceptor的intercept来获取到response,最后返回response。因为我们会向当前的interceptor传入新的next,所以在interecptor中会继续调用next.proceed()来将request传递给下一个interceptor,并通过next.proceed()返回下一个interceptor得到的response。通过这样来实现这种链式的操作。
拦截器大致的实现就讲完了,下面介绍一下之前提到过的一个Interceptor实现:retryAndFollowUpInterceptor
retryAndFollowUpInterceptor
这部分的源码呢,由于涉及到一些网络协议实现相关的东西,所以就大致讲一下一个实现流程吧,通过这个也能更好的理解一下interceptor的工作机制。最主要的方法自然是intercept,看一下源码:
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//streamAllocation主要是包含socket连接一个HTTP 请求/响应对
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()));
int followUpCount = 0;
Response priorResponse = null;
//当没有抛出异常或者需要重定向的时候一直循环
while (true) {
//请求被取消
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
//判断是否需要释放streamAllocation,如果在获得响应的过程中出现了异常,需要释放streamAllocaion的资源
boolean releaseConnection = true;
try {
//调用chain.proceed,进入后面的拦截器来对request进行拦截,并获得处理过后的response结果
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), true, request)) throw e.getLastConnectException();
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
if (!recover(e, false, request)) throw e;
releaseConnection = false;
continue;
} finally {
// 判断是否需要释放资源
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
// 如果上一次的response不为空,将其添加到当前的response中(添加重定向之前的信息),或许是协议规定的?
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
//用于判断是否需要重定向,如果需要重定向,重新生成request
Request followUp = followUpRequest(response);
//不需要重定向,直接返回response
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
//需要重定向,先把当前的response.body中的流关闭,防止泄露
closeQuietly(response.body());
//重定向次数不能超过MAX_FOLLOW_UPS
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
//判断生成的请求是否可达
if (followUp.body() instanceof UnrepeatableRequestBody) {
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
//如果重定向的url和host与之前的不相同,需要重新生成相应的streamAllocation
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(followUp.url()));
} else if (streamAllocation.stream() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
//保存当前的response作为重定向之前的response
priorResponse = response;
}
}
这个的代码虽然不少,但是不难理解,主要就是获取到chain.proceed()返回的response,然后判断得到的responseCode来决定是否需要重定向,这部分主要是通过followUpRequest()方法来进行实现的,如果不需要重定向,则返回response,如果需要,则将followUpRequest()返回的resquest作为新的请求,重新传给chain.proceed(),通过这样来实现重定向的功能。
followUpRequest()方法是对responseCode进行判断,这里就不多讲了。
关于Interceptor的介绍大概就是这样了。