Okhttp是当前最火的网络请求框架,即便是Retrofit也是在Okhttp的基础上做的进一步的封装,便于开发者的使用,本篇我们就从源码的角度出发,分析一下Okhttp网络框架的请求流程,以3.10.0为例。
先来一张流程图,然后根据流程图结合源码一步步分析:
根据上面的流程图,我们需要构建一个OkhttpClient对象,并调用newCall函数添加一个请求,就以okhttp在github上给出的get请求的示例代码为例来分析。
同步请求
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
//同步请求
Response response = client.newCall(request).execute();
return response.body().string();
}
我们一句句来分析,首先是OkhttpClient,根据OkhttpClient源码上的注释,我们知道这是一个用于生产发送HTTP请求和解析请求结果的Call对象的工厂,官方建议它应该是全局唯一的,可以被所有的HTTP Call对象复用,原因是每一个OkhttpClient都有自己的连接池和线程池,复用OkhttpClient,可以做到复用连接池、线程池,从而减小延迟,节省内存。我们看一下它的无参构造,会调用它的一个参数的构造,看一下:
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
//OkHttpClient的空构造函数,默认的interceptors是一个空集合
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = systemDefaultTrustManager();
this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}
瞬间懵逼了,其实它就是利用传入的Builder对象的参数来初始化参数(Builder模式会在下一篇分析它的设计模式的时候介绍)。好了现在参数初始化好了,我们接下来继续看,它通过Request的Builder对象来构建一个请求,主要是初始化以下参数:url、method、headers、requestBody、tag,然后调用build函数生成Request对象。下面重点要来了client.newCall(request).execute();
。先是通过OkhttpClient对象调用newCall:
@Override public Call newCall(Request request) {
//返回一个RealCall对象,Call是一个接口(这里我们传入了request)
return RealCall.newRealCall(this, request, false /* for web socket */);
}
就像开头我们说的那样,OkhttpClient是用来生产发送HTTP请求的Call实现类对象的,这里通过RealCall.newRealCall就生成了一个RealCall对象并返回,然后是调用RealCall对象的execute,我们看一下:
@Override public Response execute() throws IOException {
//加了一个同步锁,防止重复执行
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
//这个是一个辅助的监听类,不过还在预发布阶段,还不完善,所以暂时不管这个
eventListener.callStart(this);
try {
//使用OkhttpClient的Dispatcher对象来执行请求(同步中并没有什么用,异步的使用才使用到Dispatcher)
client.dispatcher().executed(this);
//通过责任链模式,从一系列的拦截器中获取到响应结果
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
//返回请求结果
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
//通知OkhttpClient的Dispatcher对象当前的请求已经完成
client.dispatcher().finished(this);
}
}
execute中的重点就在try…catch这一段,我们先来看一下client.dispatcher().executed(this);
,先是通过OkhttpClient对象的dispatcher()函数返回一个Dispatcher对象,然后调用Dispatcher对象的executed函数,我们跟进去看一下:
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
原来是将RealCall添加到了runningSyncCalls(正在运行的同步队列)里面,runningSyncCalls是ArrayDeque对象,它是一个支持两端插入和删除的队列,添加请求到队列中之后,我们怎么获取到呢,那就要看getResponseWithInterceptorChain函数了:
Response getResponseWithInterceptorChain() throws IOException {
// 构建一个保存所有拦截器对象的集合
List<Interceptor> interceptors = new ArrayList<>();
//添加我们在OkhttpClient中配置的拦截器
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) {
//添加我们在OkhttpClient中添加的网络拦截器
interceptors.addAll(client.networkInterceptors());
}
//添加调起请求服务的拦截器
interceptors.add(new CallServerInterceptor(forWebSocket));
//构建拦截器(此处的originalRequest就是我们使用okhttpClient.newCall()传入的Request对象)
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
//开始责任链调用
return chain.proceed(originalRequest);
}
到这里我们马上就要进入到深水区域了,在getResponseWithInterceptorChain中主要是作了两个事情:
(1)添加了一系列的拦截器到拦截器集合中,比如:缓存拦截器、连接拦截器、网络拦截器等
(2)开始链式调用
上面两点结合起来,其实是一个责任链模式的使用,在下一篇我们会分析。我们现在一起来看下函数中的最后两句调用,先是:
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
这句代码,主要是实例化了一个RealInterceptorChain对象,它是一个所有拦截器的聚合体(拦截器链),包含应用的所有拦截器、网络拦截器、okhttp核心等。既然拦截器链已经初始化过了,那么下一步肯定是开始依次调用拦截器链中的拦截器来处理请求,在代码中的展示就是chain.proceed(originalRequest);
,我们看一下RealInterceptorChain的proceed实现:
@Override public Response proceed(Request request) throws IOException {
//调用重载proceed函数
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
...
// 初始化下一个拦截器的RealInterceptorChain对象(这里index+1使得取得时候就是下一个拦截器。index默认从0开始)
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//获取当前的拦截器对象(并不是下一个)
Interceptor interceptor = interceptors.get(index);
//调用拦截器的intercept
Response response = interceptor.intercept(next);
// 确保下一个拦截器按要求调用chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// 确保拦截的响应结果不为null
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
这一段代码主要的逻辑有三句,分别是实例化RealInterceptorChain对象,然后获取当前的拦截器对象,最后是调用拦截器对象的intercept函数。我们重点分析一下拦截器的intercept函数,通过它就可以把整个流程串起来。以上面的示例代码为例,默认的拦截器集合会包含以下几个拦截器:
* RetryAndFollowUpInterceptor:错误重定向
* BridgeInterceptor:应用程序代码和网络代码之间的桥梁,会将应用程序的请求封装为网络请求,然后发起网络请求,最后解析网络请求的响应为应用程序的响应
* CacheInterceptor:从缓存中获取请求的响应结果和写入缓存
* ConnectInterceptor:打开到目标服务器的连接,并且会调用下一个拦截器来处理
* CallServerInterceptor:拦截器链中最后一个拦截器,真正发起网络请求的地方
下面我们针对上面的拦截器来分析一下发起一个请求在拦截器链中具体的处理流程。
RetryAndFollowUpInterceptor
@Override public Response intercept(Chain chain) throws IOException {
//此时的Chain对象是以下一个拦截器BridgeInterceptor对象封装起来的RealInterceptorChain实例
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
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 {
//调用RealInterceptorChain的proceed(会调用下一个拦截器的intercept方法)
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.getLastConnectException();
}
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.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
//处理当前的网络响应结果,根据响应码来判断是否需要后续请求。如果是正常的话,这里返回的是null
Request followUp = followUpRequest(response, streamAllocation.route());
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对象
request = followUp;
//将当前请求的响应结果赋值给priorResponse
priorResponse = response;
}
}
在intercept函数中,主要逻辑大概是:
(1)取出请求,然后调用传入的RealInterceptorChain对象的proceed函数获取到响应结果(从这里可以看出,它的响应结果来自于后面的拦截器返回,然后此处它会二次处理)
(2)在获取到响应结果之后,它会根据响应码来判断当前请求是否需要重试或者重定向。我们以followUp是否为null就可以知道
(3)如果不需要的话,调用followUpRequest返回的Request对象是null(followUp为null),后面直接返回response;
(4)如果需要,则此时返回的Request不为null,经过下面的处理之后,它会将新生成的请求以及当前的响应分别赋值给requst参数和priorResponse参数,然后继续执行,因为这是一个while(true)
的循环体,要吗得到结果return,要么抛出异常(请求不支持、重试次数超出限制等原因导致)。
RetryAndFollowUpInterceptor的intercept到这里已经完成,下面我们看一下BridgeInterceptor的intercept函数。至于怎么会跑到BridgeInterceptor里面呢,那是因为我们在每次生成RealInterceptorChain对象的时候,传入的索引都是index+1
,然后获取当前的Interceptor对象,调用其intercept。
BridgeInterceptor
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
//请求体处理(Content-Type、Content-Length、Transfer-Encoding的处理)
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) {
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");
}
//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());
}
//调用RealInterceptorChain对象的proceed函数。此时调用的就是CacheInterceptor的intercept函数
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();
}
在BridgeInterceptor的intercept函数中,主要的逻辑大概如下:
(1)将用户的请求转化为发送给服务器的请求,具体就是请求体、请求头、Cookie的处理
(2)将转换过后的请求传递给RealInterceptorChain对象的proceed函数,执行proceed函数之后会获取响应结果,它会对响应结果做处理,最后返回响应结果。
CacheInterceptor
@Override public Response intercept(Chain chain) throws IOException {
//判断缓存类是否为null,按照上面的示范代码,默认是null,因为在初始化CacheInterceptor
//类时会传入一个InternalCache对象,而在使用空构造函数创建OkhttpClient时,
//默认的Builder是没有默认的InternalCache对象的
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
//给定一个请求和缓存的响应结果,它将决定是否使用网络、缓存或者是两者都使用
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
//在网络中发送的请求,如果当前调用不需要网络,此时是null(即不需要网络,直接使用缓存即可,此时返回的networkRequest参数为null)
Request networkRequest = strategy.networkRequest;
//返回的或者经过验证缓存响应,如果当前调用没有使用缓存,返回的cacheResponse参数为null
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
cache.trackResponse(strategy);
}
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
// 如果CacheStrategy对象返回的新的请求和缓存结果都为null,说明失败了
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 (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
//调用下一个RealInterceptorChain对象的proceed函数,此时是ConnectInterceptor拦截器
networkResponse = chain.proceed(networkRequest);
} finally {
// 如果出现异常,关闭缓存体
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
//如果既使用了网络,又使用了缓存,根据返回的网络请求响应码,做不同操作
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
//此时网络返回的响应码为数据未改变。那么它会使用网络请求返回的头信息来刷新缓存的头信息。Body部分不变
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();
cache.trackConditionalCacheHit();
//刷新缓存头文件
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
//构建新的响应结果
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
//返回结果
return response;
}
CacheInterceptor的intercept主要的逻辑大概是:
(1)从当前的请求和是否存在潜在缓存标识来获取一个CacheStrategy对象
(2)从CacheStrategy对象中获取网络请求和缓存响应
(3)判断两者是否都为null,如果都为null直接返回一个响应码为504的响应
(4)如果networkRequest为null,那么就直接返回缓存的响应结果
(5)通过RealInterceptorChain对象调用ConnectInterceptor的intercept方法,获取响应结果
(6)在获取到networkResponse的时候,如果cacheResponse也不为null,那么会根据networkResponse的响应码来判断是否需要刷新缓存信息
(7)返回结果
ConnectInterceptor
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
ConnectInterceptor的intercept函数相对来说就比较简单,主要就是获取HttpCodec和RealConnection对象,然后调用最后一个RealInterceptorChain对象的proceed(因为我们并没有添加网络拦截器,所以下一个拦截器就是CallServerInterceptor)。
CallServerInterceptor
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
realChain.eventListener().requestHeadersStart(realChain.call());
//请求头的写入
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;
//判断请求体是否为null,以及当前请求的方式是否允许添加请求体(GET和HEAD是不允许的)
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
//如果请求头中包含"Expect: 100-continue",那么当请求发送出去时,会等待一个"HTTP/1.1 100 Continue"的
//响应(此时并没有发送请求体)。如果得不到,那么就返回我们没有发送请求体所获取到的结果,比如4xx
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
//读取响应头
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// 此时说明获取到"HTTP/1.1 100 Continue"的响应,写入请求体
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!connection.isMultiplexed()) {
//没有获取到"HTTP/1.1 100 Continue"的响应,防止HTTP/1连接被重用。
streamAllocation.noNewStreams();
}
}
httpCodec.finishRequest();
if (responseBuilder == null) {
//在GET、HEAD请求不会走上面那一段请求体相关的逻辑,正常都会走到这
//需要发送请求体的请求,在发送出去请求体之后之后,会走到这里
realChain.eventListener().responseHeadersStart(realChain.call());
//读取响应头
responseBuilder = httpCodec.readResponseHeaders(false);
}
//构建响应结果
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.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
responseBuilder = httpCodec.readResponseHeaders(false);
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
realChain.eventListener()
.responseHeadersEnd(realChain.call(), 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(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
//返回结果
return response;
}
在CallServerInterceptor的intercept函数中,主要逻辑大概如下:
(1)写入请求头
(2)判断是否需要写入请求体。GET和HEAD是不需要的
(3)在需要写入请求体的逻辑中,会先判断请求头中是否包含”Expect: 100-continue”,包含的话,当请求发送出去时,会等待一个”HTTP/1.1 100 Continue”的响应(此时并没有发送请求体)。如果得不到该响应,那么就返回我们没有发送请求体所获取到的结果,比如4xx
(4)在不需要请求体,或者已经完成请求体逻辑的情况下。读取响应头,构建响应结果(此时是不包含响应体的)
(5)写入响应体,它根据是否是WebSocket所有区分。
(6)返回响应结果
到这里所有的拦截器它们的功能和大体流程就都已经分析完了。同步请求到这里也完了,因为通过拦截器返回的结果已经通过getResponseWithInterceptorChain函数返回给execute方法。下面我们来分析一下异步请求。
异步请求
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
前面的和同步请求是一样的,我们从不一样的地方开始分析。我们看一下RealCall的enqueue函数:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
//调用分发器Dispatcher对象的enqueue函数入队
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
通过上面的代码我们可以发现,它其实就是通过OkhttpClient对象获取到分发器对象,然后将当前这个异步请求添加到分发器中。这里的分发器就是Dispatcher,它内部维持着一个线程池,该线程池的特点是:
(1)核心线程数为0
(2)可以根据需要近似无限创建线程,因为最大线程数为Integer.MAX_VALUE
(3)空闲时间超过60秒自动回收
(4)队列为SynchronousQueue,其特点是没有数据缓冲(队列只能存储一个元素),生产者线程对其的插入操作必须等待消费者的移除操作,反过来也一样,消费者移除数据操作必须等待生产者的插入。一种一对一的交付行为
线程池的特点说明好了,我们开始看enqueue的源码:
synchronized void enqueue(AsyncCall call) {
//校验请求是否超标,默认的最大并发数为64,同一主机最大并发数为5
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//添加到正在执行的异步任务队列中
runningAsyncCalls.add(call);
//调用线程池执行
executorService().execute(call);
} else {
//超过最大并发数,先添加到等待队列中
readyAsyncCalls.add(call);
}
}
enqueue里面的逻辑很简单,判断是否超过最大并发数,超过添加到等待队列中;没有超过添加到执行队列中,同时调用线程池的execute来执行Runnable。Runnable是什么鬼,我们传入的不是AsyncCall吗,我们看一下AsyncCall这个类,AsyncCall集成自NamedRunnable,NamedRunnable继承自Runnable,只不过NamedRunnable多了一个为线程命名的功能,并重写了run,但是它保留了一个execute抽象函数。我们看一下AsyncCall的execute即可:
@Override protected void execute() {
boolean signalledCallback = false;
try {
//多么熟悉的字眼啊。感动中。。。这个我们在同步请求中已经分析了,如果有疑问翻到上面在看一下。
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
//请求取消,走失败回调
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
//返回响应结果
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
//通知分发器当前请求已经完成
client.dispatcher().finished(this);
}
}
好吧,到这里异步请求也完成了。能看到这里也很不容易,感谢百忙之中抽空看本文。如果有什么不足之处还请指出。