在上一篇文章中 主线流程源码分析 中, 我们了解到了OkHttp框架的流程和原理. 并在最后留下了一个知识分块, 那就是 "拦截器"
我们在源码中得知到Response响应体其实是经过一个由责任链设计模式设计出来的方法中getResponseWithInterceptorChain()生成并返回的, 而本文的目标就是要剖析这拦截器到底做了什么
先看看这个方法有什么
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List interceptors = new ArrayList<>();
// 开发者自定义拦截器,在OkHttpClient构建的时候设置的
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));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
Response response = chain.proceed(originalRequest);
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);
}
}
}
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
在这里基本得知 有四个核心拦截器. 分别为"重试/重定向拦截器" "请求信息处理拦截器" "缓存拦截器" 和 "连接服务器请求拦截器".
通过拦截器设计模式 每个拦截器自身都有决定是否处理事件以及具体逻辑的能力, 我们先一步一步来看看.
在代码中 拦截器集合被添加完之后, 会生成一个Intercepor内部类Chain的对象. 这Chain实际上是一个接口, 所以按照代码逻辑来分析, 真正的实现类其实是RealInterceptorChain. 而Response则是从Chain的proceed方法得来的. 所以我们现在看看RealInterceptorChain里面的proceed方法干了什么
public final class RealInterceptorChain implements Interceptor.Chain {
// RealCall传进来的拦截器集合
private final List interceptors;
private final Transmitter transmitter;
private final @Nullable Exchange exchange;
// 当前下标,如果是RealCall传过来的就是0,如果是自身递归执行的就是不断+1
private final int index;
private final Request request;
private final Call call;
private final int connectTimeout;
private final int readTimeout;
private final int writeTimeout;
private int calls;
public RealInterceptorChain(List interceptors, Transmitter transmitter,
@Nullable Exchange exchange, int index, Request request, Call call,
int connectTimeout, int readTimeout, int writeTimeout) {
this.interceptors = interceptors;
this.transmitter = transmitter;
this.exchange = exchange;
this.index = index;
this.request = request;
this.call = call;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
this.writeTimeout = writeTimeout;
}
@Override public @Nullable Connection connection() {
return exchange != null ? exchange.connection() : null;
}
@Override public int connectTimeoutMillis() {
return connectTimeout;
}
@Override public Interceptor.Chain withConnectTimeout(int timeout, TimeUnit unit) {
int millis = checkDuration("timeout", timeout, unit);
return new RealInterceptorChain(interceptors, transmitter, exchange, index, request, call,
millis, readTimeout, writeTimeout);
}
@Override public int readTimeoutMillis() {
return readTimeout;
}
@Override public Interceptor.Chain withReadTimeout(int timeout, TimeUnit unit) {
int millis = checkDuration("timeout", timeout, unit);
return new RealInterceptorChain(interceptors, transmitter, exchange, index, request, call,
connectTimeout, millis, writeTimeout);
}
@Override public int writeTimeoutMillis() {
return writeTimeout;
}
@Override public Interceptor.Chain withWriteTimeout(int timeout, TimeUnit unit) {
int millis = checkDuration("timeout", timeout, unit);
return new RealInterceptorChain(interceptors, transmitter, exchange, index, request, call,
connectTimeout, readTimeout, millis);
}
public Transmitter transmitter() {
return transmitter;
}
public Exchange exchange() {
if (exchange == null) throw new IllegalStateException();
return exchange;
}
@Override public Call call() {
return call;
}
@Override public Request request() {
return request;
}
@Override public Response proceed(Request request) throws IOException {
return proceed(request, transmitter, exchange);
}
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
// 一开始传进来的index为0 所以肯定不会抛出异常
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// 在这里new一个新的节点, 其实就是链路中的下一个RealInterceptorChain, 犹如递归似的, 其中index自身+1处理
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
// 根据当前下标取出对应的拦截器
Interceptor interceptor = interceptors.get(index);
// 用对应的拦截器,把下一个节点丢进去进行拦截处理
Response response = interceptor.intercept(next);
// 把得到的response进行返回
return response;
}
}
这似乎相对比较抽象, 那我们捋一下思路先, 按照okhttp的源码规定来看, 4个拦截器其实是按顺序添加的. "1重试 -> 2请求信息 -> 3缓存 -> 4请求" 就相当于链路的生成是从1到4往下走的, 而走的过程中, 会对response进行处理加工/返回. 并最终从后往前执行. 当然 并不一定是4->3->2->1 , 假如是有缓存拦截器, 已经拦截到有response, 此时就会直接往回调, 而不会专门去请求了.
具体我们针对这4个拦截器进行逐一分析吧.
重试拦截器里面其实是有一个while死循环, 如果请求过程中出了连接问题, 那么就会自行恢复连接并尝试往后传递, 直到整个请求结束后都没出问题为止
public final class RetryAndFollowUpInterceptor implements Interceptor {
// 重试代码比较多 所以砍了一大堆看起来不大相干的代码了 只留一些有用的
// 在这里开始做拦截
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
// 这里弄了个死循环
while (true) {
// 响应信息
Response response;
try {
// 这里往后传递
response = realChain.proceed(request, transmitter, null);
success = true;
} catch (RouteException e) {
// 这里就是在请求过程中, 连接失败了, 就会走recover方法进行尝试重连
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
// 然后本次循环结束 再循环一次
continue;
} catch (IOException e) {
// 这里就是在请求过程中, 连接失败了, 就会走recover方法进行尝试重连
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, transmitter, requestSendStarted, request)) throw e;
// 然后本次循环结束 再循环一次
continue;
} finally {
// The network call threw an exception. Release any resources.
if (!success) {
transmitter.exchangeDoneDueToException();
}
}
Exchange exchange = Internal.instance.exchange(response);
Route route = exchange != null ? exchange.connection().route() : null;
Request followUp = followUpRequest(response, route);
if (followUp == null) {
if (exchange != null && exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
return response;
}
RequestBody followUpBody = followUp.body();
if (followUpBody != null && followUpBody.isOneShot()) {
return response;
}
}
}
通过这个信息拦截器, 会对response进行各种请求头的添加. 添加完之后, 继续往后传递.
public final class BridgeInterceptor implements Interceptor {
// 在这里接收拦截
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
// 取得请求体之后, 会对请求体进行各种的header信息操作, 也就是说可以在这里插入请求信息
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
...
...
...
// 同样 也是在这里往下一个拦截器传递
Response networkResponse = chain.proceed(requestBuilder.build());
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
//处理完之后 返回
return responseBuilder.build();
}
/** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */
private String cookieHeader(List cookies) {
StringBuilder cookieHeader = new StringBuilder();
for (int i = 0, size = cookies.size(); i < size; i++) {
if (i > 0) {
cookieHeader.append("; ");
}
Cookie cookie = cookies.get(i);
cookieHeader.append(cookie.name()).append('=').append(cookie.value());
}
return cookieHeader.toString();
}
}
这里面 就会根据当前到底有没有网络/有没有缓存进行各种综合考虑, 比如没网又没缓存, 就会返回报错response. 如果没网有缓存 ,就取缓存response. 如果有网, 那就会往后传递(下一步就是网络请求拦截器), 如果有缓存更新 那就会更新一下再返回response.
public final class CacheInterceptor implements Interceptor {
final @Nullable InternalCache cache;
public CacheInterceptor(@Nullable InternalCache cache) {
this.cache = cache;
}
@Override public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
// 获取网络请求体, 如果当前没网会为空
Request networkRequest = strategy.networkRequest;
// 缓存响应体, 如果当前没缓存则会为空
Response cacheResponse = strategy.cacheResponse;
// 如果当前又没网,缓存又没存,直接就当场返回个错误的reponse回去了.
// If we're forbidden from using the network and the cache is insufficient, fail.
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// 如果单纯没网, 那就用缓存的请求响应体返回回去.
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
// 如果有网络,那就还是会往后传递
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// 如果是有缓存的,那就判断下是否有改动的
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
// 如果是有改动的话,那就会更新一下缓存对象 再返回回去
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
}
}
这个拦截器就是最终的拦截器, 也就是请求并组装响应体的拦截器. 注意这是最后一个拦截器, 所以不会再有proceed下一步传递的操作了.
public final class CallServerInterceptor implements Interceptor {
// 在这里接收拦截
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Exchange exchange = realChain.exchange();
Request request = realChain.request();
...
...
...
// 就是在这里 根据exchange以及request等信息 创建response对象
Response response = responseBuilder
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
// 然后后面的这些 应该就是对请求回来的信息进行各种判断和组装了
int code = response.code();
if (code == 100) {
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
response = exchange.readResponseHeaders(false)
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
exchange.responseHeadersEnd(response);
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(exchange.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
exchange.noNewExchangesOnConnection();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
// 最终就可以在这里返回response
return response;
}
}
其实上述描述中 应该还有不少地方是有遗漏的 具体每个拦截器的每一行代码还没专门去细致读解. 主要是为了了解这些拦截器之间的关系 以及负责的职责.
其中有很多地方是没注意到的, 甚至理解可能会有误, 还请各位读者dalao们多多指点多多批评.