之前分析了okHttp的同步跟异步流程分析,都是经过层层封装,然后卡在拦截器这里,关键封装请求头,建立连接,获取数据的部分全在拦截器这部分,由于拦截器较多,分析同步,异步的时候并没有详细解析,在这里,一一解析。
回顾下getResponseWithInterceptorChain()方法:
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
......
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List 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 (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
interceptors是list集合,首先就把自定义的拦截器add进去了,在这里,假装没有写自定义拦截器,来看看retryAndFollowUpInterceptor。看看注释:This interceptor recovers from failures and follows redirects as necessary。很明显,这个拦截器是用来负责失败重试以及重定向的,我们来看看关键代码:
public final class RetryAndFollowUpInterceptor implements Interceptor {
......
......
@Override public Response intercept(Chain chain) throws IOException {
//获取请求体request
Request request = chain.request();
//获取拦截器链对象,这个很重要,是用来运行完拦截器集合的关键(内部的proceed()方法)
RealInterceptorChain realChain = (RealInterceptorChain) chain;
//这个call之前介绍过,在这里获取这个call对象
Call call = realChain.call();
//状态监听
EventListener eventListener = realChain.eventListener();
//连接池,获取一个正确的连接
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
int followUpCount = 0;
Response priorResponse = null;
while (true) { //开始循环
if (canceled) { // 判断请求是否取消
streamAllocation.release(); //连接被释放
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
//调用下一个拦截器
response = realChain.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(), streamAllocation, false, request)) {
throw e.getFirstConnectException();
}
releaseConnection = false;
//出现异常,重试
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
//出现连接关闭异常,尝试恢复
if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
releaseConnection = false;
continue; //继续重试
} finally {
// We're throwing an unchecked exception. Release any resources. //关闭资源
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
//前一个重试获取的response存在
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Request followUp;
try {
//封装新的重试Request,为它添加一些内容,比如验证头
followUp = followUpRequest(response, streamAllocation.route());
} catch (IOException e) {
streamAllocation.release();
throw e;
}
//请求体为空,返回response。莫非是请求成功,请求体是空的?反正我是这么想的。
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
closeQuietly(response.body());
//重试次数超过允许最大次数,释放连接,抛出异常
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
}
这个拦截器,从上往下看下来,可以这么理解,当一个请求,由于各种原因,获取数据失败了,就尝试着去重连恢复。否则就重新封装request(followUp),通过拦截器链继续执行这个请求。源码中有个
if(followUp == null) 就返回response,当followUp == null的时候,这个重试流程就结束了。
很好,接下来,我们来看BridgeInterceptor,先来看下一张图(网络上找的):
图中很清晰的说明这个拦截器的作用:给request添加请求头,给resonpse添加响应头。
放码过来吧(关键代码):
public final class BridgeInterceptor implements Interceptor {
......
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
//给request添加contentType
RequestBody body = userRequest.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
long contentLength = body.contentLength();
if (contentLength != -1) { //如果body有值,请求头设置body的内容长度,并且移除传输编码
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else { //否则,设置传输编码(这里是分块传输编码),移除内容长度值
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
List cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
//这里开始,是给response添加header
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();
}
}
有点意思,专门搞个拦截器,给请求前的request,返回后的response添加header内容。
篇幅过长,下篇继续分析后面的拦截器,预知后事如何,且看下集分解。