接着上篇文章,我们来看RealCall.getResponseWithInterceptorChain()
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
return chain.proceed(originalRequest);
}
可以看到,这个创建了一个ApplicationInterceptorChain,这是一个拦截器链,并执行了其proceed方法。
再来看chain.proceed()
@Override public Response proceed(Request request) throws IOException {
// If there's another interceptor in the chain, call that.
// client是OkHttpClient
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}
return interceptedResponse;
}
// No more interceptors. Do HTTP.
return getResponse(request, forWebSocket);
}
}
可以看到,这里从client取出intercepter拦截器集合,调用拦截器的intercept方法。
拦截器是一种能够监控、重写、重试调用的机制。同来添加、移除、转换请求和相应的头部信息。
如果有多个拦截器,那么,在第一个拦截器的intercept()中,会再调用chain.proceed(request)
,从而在此处阻塞,第二个拦截器再执行。
之后如果最终的Response不为null,则返回这个response。否则就直接调用getResponse()进行真正的网络处理。
我们自定义一个请求和相应的拦截器,以这个拦截器为例
class LoggingInterceptor implements Interceptor {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
logger.info(String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
logger.info(String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
}
}
可以看到,拦截器内部可以在chain.proceed(request)
的前后,增加一些代码,这显然就是一个代理模式,有需要的时候,还可以控制chain.proceed(request)
是否执行。
总结
拦截器可以在执行真正的请求之前,添加、移除、转换请求和响应的头部信息,通过不断调用proceed()
,更改index,从而轮询所有的拦截器,如果没有更多的拦截器,最终就会执行网络请求。
源码分析中的OkHttp版本为com.squareup.okhttp3:okhttp:3.2.0