目录
前言
前面介绍了OkHttp之Dispatcher, Dispatcher主要就是对异步请求进行分发和执行,那么对于OkHttp到底是怎么进行完成忘了请求的呢?主要实现就是getResponseWithInterceptorChain()这个里面的内容。
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 (!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);
}
拦截器的加入
从源码中看到就是添加了一系列的拦截器,依次加入的拦截器为:
1)用户自定义的拦截器,注意要实现Interceptor.Chain接口,并且要取调用下一个拦截器。可以在请求服务器之前做一些自己的逻辑处理。
2)RetryAndFollowUpInterceptor:失败之后自动重链和重定向
3)BridgeInterceptor:数据转换,将用户设置的Request转换成网络请求所需要的请求,传到下一个拦截器中,并将下一个拦截器返回的结果转换成RealResponseBody返回给上一个拦截器。
4)CacheInterceptor:缓存。读取缓存直接返回和更新缓存,并将其下一个拦截器返回的结果返回给上一个拦截器。
5)ConnectInterceptor:创建一个链接,并将其下一个拦截器返回的结果返回给上一个拦截器
6)CallServerInterceptor:向服务器请求数据,完成请求,并返回给上一个拦截器。
最后将这些拦截器都传入到RealInterceptorChain,通过chain.proceed()来完成整个请求过程。而这些拦截器中采用的是责任链模式,一个拦截器会去调用下一个拦截器的intercept()或.proceed()来完成最后的请求。
我们就分别分析下各个拦截器。
RealInterceptorChain
拦截器执行的起始。在将一系列的拦截器加入到interceptors集合中之后,从这里的proceed()开始执行。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
// .....省略代码
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// .....省略代码
return response;
}
我们发现就是从 interceptors集合中取出加进去的第一个拦截器,然后执行拦截器的intercept()的方法。从上面的分析中我们可以看到除去用户自定义的拦截器,那么就是RetryAndFollowUpInterceptor了,所以就调用了RetryAndFollowUpInterceptor的intercept()方法,同时将RetryAndFollowUpInterceptor返回的Response返回给RealInterceptorChain。
RetryAndFollowUpInterceptor
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
//1)创建StreamAllocation
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
int followUpCount = 0;
Response priorResponse = null;
while (true) {
//....省略代码
Response response;
boolean releaseConnection = true;
try {
//2)调用下一个拦截器CacheInterceptor
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
//....省略代码
} finally {
//....省略代码
}
//上一次的response若存在,则将上一次的response的body置为null,赋值到response
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
//3)根据response的响应码和响应头,查看是否需要重定向,并返回获取request
Request followUp;
try {
followUp = followUpRequest(response, streamAllocation.route());
} catch (IOException e) {
streamAllocation.release();
throw e;
}
//4)如果没有request返回,则不需要重定向,直接返回
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
closeQuietly(response.body());
//5)查看是否超出重链的最大次数,抛出异常
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
//....省略代码
//6)检查如果不是相同的连接,则重建
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
}
//....省略代码
//7)重新设置requst,并把当前的response赋值给到priorResponse,继续while循环
request = followUp;
priorResponse = response;
}
}
从源码的注释中可以看到,该拦截器主要用来失败之后重链或者重定向。
1)创建StreamAllocation
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
//创建StreamAllocation
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
在拦截器中创建StreamAllocation。而StreamAllocation主要用来建立执行HTTP请求所需要网络设施的组件。即完成为每个请求(call)来寻找连接connection并建立传输流codec。所以在StreamAllocation中包含着请求call(Call)、传输流codec(HttpCodec)、连接connection(RealConnection)、连接池connectionPool(ConnectionPool),对应的获取流的方法为newStream(),找到合适连接方法findConnection()以及完成请求任务之后的一些关闭流对象finish()、终止、取消以及释放资源的方法。
简单介绍下StreamAllocation的几个关键代码,创建stream的创建过程如下:对应着newStream()
public HttpCodec newStream(
OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
//.....省略代码
try {
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
}
//.....省略代码
}
在newStream()中主要就是通过findHealthyConnection()找到一个可用的连接,并且进行建立连接,而该方法返回的流HttpCodec,如果是Http1.1/1.0则返回的是Http1Codec,如果是Http2.0则返回的是Http2Codec。
1)流Codec,假设返回的是Http1Codec
在RealConnection中的establishProtocol()中会根据不同的情况确定是使用Http1.0/1.1协议还是Http2.0协议。这里面提供了一些网络请求的基本方法,像发送请求的时候写入请求头部、创建请求体等,后面结合CallServerInterceptor具体看下里面的各个方法的作用。
2)简单的说下findHealthyConnection()。最终调用的就是findConnection()
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
//.......省略代码
// 前面通过一系列的逻辑找到一个可用的connection,建立起连接
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
//.......省略代码
}
该方法主要就是找到可用的connection,建立起连接。首先会查看已经存在的connection是否可用,如果可用,则直接返回;否则就从connectionPool中找到可用的connection,如果还是找不到则通过new RealConnection来创建connection,通过这种方法创建的连接需要通过acquire关联到connection.allocations上 。
我们可以看到在result.connect()调用的过程中,已经将请求call传入到了connection中。另外还要注意一点,这个里面提到的conectionPool是从上一个拦截器中传入的OkHttpClient中获取的,而conectionPool在创建OkHttpClient中被实例化的。所以如果OkHttpClient全局只保持一个实例的化,那么这个链接池conectionPool也只有这一个实例。
这个newStream()的真正调用就在ConnectInterceptor连接器中,代码后面在介绍。
综上,在RetryAndFollowUpInterceptor这个拦截器中仅仅是创建了StreamAllocation对象,把这个对象传入了下一个拦截器。而StreamAllocation负责为该次请求找到可用的连接,并建立连接。
2)其他几个步骤
从其他几个过程中看来,这里就是建立了一个while(true)的循环来进行重试。
(1)如果可以正常调用下一个拦截器,则中断该循环,进入到下一个拦截器中;
(2)如果不能去调用到下一个拦截器,那么就要看看是否保存有前一次的priorResponse,构建一个body为null的response,然后判断followUp请求是否需要重定向,若不需要则直接将该response返回
(3)如果需要的化,则去检查重定向次数是否超出了最大次数,如果是,则抛出异常
(4)否则就去检查是否有相同的链接,如果不是则需要重新创建StreamAllocation
(5)重新设置request和priorResponse进行下一次重试。
3)总结
RetryAndFollowUpInterceptor这个拦截器就是实例化了StreamAllocation对象传入到下一个拦截器中,并且保存上一次的response,不断的根据response返回的响应码来确定是否进行重试。
RetryAndFollowUpInterceptor拦截器通过RealInterceptorChain的 realChain.proceed(),因为此时RealInterceptorChain的实例是从之前调用RetryAndFollowUpInterceptor的时候传入的,所以此时RealInterceptorChain中会调用到了BridgeInterceptor。
BridgeInterceptor
就是将用户设置的数据转换成网络请求所用的数据,并调用到下一个拦截器,并将返回的结果根据是否需要gzip来进行压缩。
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
if (body != null) {
//设置request的header
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
//......省略代码 就是设置request的一些header
//加载cookie
List<Cookie> 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 networkResponse = chain.proceed(requestBuilder.build());
//将信息保存到Cookie中
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
//对拦截器返回的response进行处理。如果支持gzip压缩,则有Okio处理
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();
}
1)主要设置了header的Content-Type、Content-Length、Transfer-Encoding、Host、Connection为Keep-Alive、Accept-Encoding、Cookie、User-Agent
2)判断是否需要加载Cookie
3)调用下一个拦截器,返回networkResponse
4)若有Cookie则将信息保存到Cookie中
5)判断networkResponse是否支持gzip压缩,如果支持则通过Okio进行压缩
6)否则直接返回response给上一个拦截器。
这个CookieJar是通过构造函数传入的
public BridgeInterceptor(CookieJar cookieJar) {
this.cookieJar = cookieJar;
}
显然在RealCall在将 BridgeInterceptor加入到拦截器集合的时候,从OkHttpClient中获取的,跟踪到源码中发现,其实OkHttp默认的是没有Cookie的
public Builder() {
//....省略代码
cookieJar = CookieJar.NO_COOKIES;
//....省略代码
}
我们看到BridgeInterceptor同样也是通过调用RealInterceptorChain的 realChain.proceed(),调用到了下一个拦截器CacheInterceptor
其他拦截器见OkHttp之getResponseWithInterceptorChain(二)