
前言
做React Native的時候遇到業(yè)務(wù)線反饋的一個Bug:在使用Charles做代理的時候,將reactTimeout值改小的時候,有時候會發(fā)現(xiàn)在Charles沒有捕獲到Http請求的時候,仍然返回數(shù)據(jù)了。這是一個比較詭異的問題,出現(xiàn)問題的原因可能有以下兩點(diǎn):
- Http請求通過緩存直接返回;
- Http請求并未通過設(shè)置的代理請求。
針對這個問題,用了STFW沒有找到什么明確的答案,既然如此,那就直接從源碼著手分析,通過一些筆記的整理,就有了這篇文章。由于精力能力有限,對于OkHttp3的分析不會在細(xì)節(jié)處深入,如果有錯誤指出煩請拍磚,共同進(jìn)步。
(另源碼分析基于OkHttp3-3.4.1版本)
概述
本文會先簡單說下OkHttp3的工作流程,然后介紹OkHttp3的一些核心類(如連接池StreamAllocation以及各式各樣的Interceptor),接著從源碼角度分析一次HTTP請求在OkHttp3中所經(jīng)歷的過程,在不同的Interceptor(攔截器)可以看到一些OkHttp3設(shè)計的一些巧妙思想,最后對上述分析做個簡單的總結(jié)。
Okhttp3是Square公司開源的強(qiáng)大高效的Java網(wǎng)絡(luò)請求庫,具有以下特性:
- 支持Http2/SPDY;
- 默認(rèn)啟用長連接,使用連接池管理,支持Cache(目前僅支持GET請求的緩存);
- 路由節(jié)點(diǎn)管理,提升訪問速度;
- 透明的Gzip處理,節(jié)省網(wǎng)絡(luò)流量。
- 靈活的攔截器,行為類似Java EE的Filter或者函數(shù)編程中的Map。
旅程開始
OkHttp3支持同步和異步兩種請求方式,異步請求會經(jīng)過Dispatcher在線程池中執(zhí)行。同步請求沒有線程池這一個過程,由于同步請求很簡單,這里僅分析異步請求方式。OkHttp3一次完整的請求過程是從構(gòu)造一個Request對象開始,接著調(diào)用OkHttpClient.newCall()返回一個RealCall對象并調(diào)用RealCall.enqueue()方法,最后會進(jìn)入Dispacher.enqueue()方法中,這里會將RealCall對象放入線程池中調(diào)度執(zhí)行。

OkHttp3的核心類
這部分會簡單介紹一些OkHttp3的核心類,這些核心類共同支持了OkHttp3的一些基礎(chǔ)功能。粗略的分成了Interceptor(攔截器)、Router(路由)和Stream(流)三部分,OkHttpClient類是初始化時候就配置的,比較簡單就不說了。

Interceptor(攔截器)
OkHttp3的Interceptor是Request -> Response請求過程中的一個"節(jié)點(diǎn)"單位,通過一連串有序的Interceptor攔截器"節(jié)點(diǎn)"組成一條加工鏈,加工鏈中的任意一個"節(jié)點(diǎn)"都可以去攔截加工Request和Response。OkHttp默認(rèn)提供了一套完善的Interceptor集合,當(dāng)然也支持自定義一個Interceptor來實(shí)現(xiàn)一個上傳/下載的進(jìn)度更新器或者黑白名單攔截等等個性化的功能。
閱讀OkHttp3的源碼建議從Interceptor開始。
OkHttp默認(rèn)提供了如下Interceptor:
RetryAndFollowUpInterceptor:默認(rèn)情況下位于OkHttp3加工鏈的首位,顧名思義,具有失敗-重試機(jī)制,支持頁面重定向和一些
407之類的代理驗(yàn)證等,此外負(fù)責(zé)StreamAllocation對象的創(chuàng)建(稍后介紹)。BridgeInterceptor:橋攔截器,配置Request的Headers頭信息:讀取Cookie,默認(rèn)啟用
Gzip,默認(rèn)加入Keep-Alive長連接,如果不想讓OkHttp3擅自使用長連接,只需在Request的Header中預(yù)設(shè)Connection字段即可。CacheInterceptor: 管理OkHttp3的緩存,目前僅支持
GET類型的緩存,使用文件形式的Lru緩存管理策略,CacheStrategy類負(fù)責(zé)了緩存相關(guān)的策略管理。ConnectInterceptor:OkHttp3打開一個Socket連接的地方,OkHttp3相關(guān)的Router路由切換策略也可以從這里開始跟蹤。
CallServerInterceptor:處于OkHttp3加工鏈的末尾,通過HttpStream往Socket中寫入Request報文信息,并回讀Response報文信息。
OkHttp3的攔截器執(zhí)行順序依次是:自定義Interceptors(暫且稱作
A) ->RetryAndFollowUpInterceptor->BridgeInterceptor->CacheInterceptor->ConnectInterceptor-> 自定義NetInterceptors(暫且稱作B) ->CallServerInterceptor
B僅在非WebSocket情況下被調(diào)用。A與B的區(qū)別是,A能攔截所有類型的請求,包括緩存命中的請求;而B僅攔截非WebSocket的情況下產(chǎn)生真正網(wǎng)絡(luò)訪問的請求。因此在B上做網(wǎng)絡(luò)上傳和下載進(jìn)度的監(jiān)聽器是比較合適的。
Stream(流相關(guān)類)
OkHttp3并沒有直接操作Socket,而是通過okio庫進(jìn)行了封裝,okio庫的設(shè)計也是非常贊的,它的Sink對應(yīng)輸入流,Source對應(yīng)輸出流,okio已經(jīng)實(shí)現(xiàn)了與之對應(yīng)的緩沖相關(guān)的包裝類,采用了Segment切片和循環(huán)鏈表結(jié)構(gòu)實(shí)現(xiàn)緩沖處理,有興趣還是可以看看okio的源碼。
StreamAllocation:OkHttp3管理物理連接的對象,負(fù)責(zé)連接流的創(chuàng)建關(guān)閉等管理工作,通過池化物理連接來減少Hand-shake握手過程以提升請求效率,另外StreamAllocation通過持有RouteSelector對象切換路由。關(guān)于路由切換,這里有個場景,在Android系統(tǒng)中,如果你配置了代理,當(dāng)代理服務(wù)器訪問超時的時候,OkHttp3在進(jìn)行請求重試時候會切換到下個代理或者采用無代理直連形式請求。因此并非設(shè)置了代理,OkHttp3就會"老實(shí)"的跟著你的規(guī)則走。這也是本文一開始提到的問題產(chǎn)生的原因。
HttpStream: 這是一個抽象類,其子類實(shí)現(xiàn)了各類網(wǎng)絡(luò)協(xié)議流格式。 HttpStream在OkHttp3中有兩個實(shí)現(xiàn)類Http1xStream和Http2xStream,Http1xStream實(shí)現(xiàn)了
HTTP/1.1協(xié)議流,Http2xStream則實(shí)現(xiàn)了HTTP/2和SPDY協(xié)議流。ConnectionPool:OkHttp連接池,由OkHttpClient持有該對象,ConnectionPool持有一個0核心線程數(shù)的線程池(與
Executors.newCachedThreadPool()提供的線程池行為完全一樣)用于清理一些超時的RealConnection連接對象,持有一個Deque對象緩存OkHttp3的RealConnection連接對象。
Router(路由)
OkHttp3要求每個連接都需要指明一個Router路由對象,當(dāng)然這個Router路由對象可以是直連類型的,意即你不使用任何的代理服務(wù)器。當(dāng)嘗試使用某個路由請求失敗的時候,OkHttp3會在允許請求重試的情況下通過RouterSelector切換到下個路由繼續(xù)請求,并將失敗的路由記錄到黑名單中,這樣在OkHttp3重復(fù)請求一個目標(biāo)地址的時候能夠優(yōu)先選擇成功的路由進(jìn)行網(wǎng)絡(luò)請求。
- Router:包含代理與Socket地址信息。
- RouteDatabase:記錄請求失敗的Router路由對象的"黑名單"。
- RouteSelector:負(fù)責(zé)指派Router路由。持有RouteDatabase對象。
源碼角度解析OkHttp3請求
分析查看源碼一般抓主干,本文也不例外,我們接下來就直接從代碼來看OkHttp3是如何進(jìn)行一次完整的網(wǎng)絡(luò)請求。限于篇幅,僅僅分析異步情況下的網(wǎng)絡(luò)請求,同步方式的網(wǎng)絡(luò)請求更加簡單,核心部分都是一樣的。另外為了方便閱讀,部分無關(guān)語句會被省略。
//所在類 okhttp3.RealCall
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
在調(diào)用client.newCall(request).enqueue(...)方法開始請求之后,就會進(jìn)入上面的enqueue()方法,可以看到最后是調(diào)用了Dispatcher.enqueue(),繼續(xù)跟蹤源碼到如下地方:
//所在類 okhttp3.Dispatcher
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
可以看到OkHttp3使用了一個線程池來執(zhí)行這個AsyncCall,AsyncCall本質(zhì)上是一個Runnable對象,最后會調(diào)用AsyncCall.execute()方法。
//所在類 okhttp3.RealCall.AsyncCall
@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) {
...
} finally {
client.dispatcher().finished(this);
}
}
通過getResponseWithInterceptorChain()獲取Response報文,繼續(xù)跟蹤下去。
//所在類 okhttp3.RealCall
private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
OkHttp3將Interceptor(攔截器)放到一個集合里,通過自增遞歸的方式調(diào)用RealInterceptorChain.proceed()方法依次執(zhí)行集合里的每一個Interceptor(攔截器),這個Chain的傳遞過程剛開始看有點(diǎn)繞,這里簡單畫個流程圖方便理解。

RealInterceptor關(guān)鍵源碼如下:
// 所在類 okhttp3.internal.http.RealInterceptorChain
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpStream httpStream, Connection connection, int index, Request request) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpStream = httpStream;
this.index = index;
this.request = request;
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
Connection connection) throws IOException {
// 省略部分源碼
...
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpStream, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpStream != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
RealInterceptorChain的構(gòu)造函數(shù)中的index和interceptors是匹配的,用來索引接下來需要執(zhí)行的Intercetpor攔截器。
通過調(diào)用interceptor.intercept(next)方法,在各個Intercetpor攔截器里又能通過next參數(shù)繼續(xù)調(diào)用proceed()方法,完成遞歸操作。
接下來我們按照執(zhí)行順序依次看下這些interceptors具體都干了什么,從RetryAndFollowUpInterceptor開始。
// 所在類 okhttp3.internal.http.RetryAndFollowUpInterceptor
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()));
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
boolean releaseConnection = true;
try {
response = ((RealInterceptorChain) chain).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(), true, request)) throw e.getLastConnectException();
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
if (!recover(e, false, 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();
}
Request followUp = followUpRequest(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) {
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()));
} else if (streamAllocation.stream() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
代碼有點(diǎn)長,不過主要還是做了這幾個操作,首先StreamAllocation對象在這里被創(chuàng)建,接著調(diào)用proceed()方法執(zhí)行了一次請求,并拿到一個Response報文,在followUpRequest()方法中對Response報文進(jìn)行了各種判斷(驗(yàn)證了407,判斷需不需要重定向等)確定是否需要再次請求,如果需要持續(xù)請求會在followUpRequest()返回一個新的Request對象并重新請求。followUpRequest()的代碼有點(diǎn)長,可以自行查閱源碼,這里就不貼了。繼續(xù)看執(zhí)行的下一個攔截器BridgeInterceptor。
// 所在類 okhttp3.internal.http.BridgeInterceptor
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
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) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
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());
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);
responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
可以看出來BridgeInterceptor對Request和Response報文加工的具體步驟,默認(rèn)對Request報文增加了gzip頭信息,并在Response報文中對gzip進(jìn)行解壓縮處理。另外CookieJar類也是在這里處理的。接下來就是CacheInterceptor攔截器。
// 所在類 okhttp3.internal.cache.CacheInterceptor
@Override public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
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.
}
// 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(EMPTY_BODY)
.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 (validate(cacheResponse, networkResponse)) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.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());
}
}
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (HttpHeaders.hasBody(response)) {
CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
response = cacheWritingResponse(cacheRequest, response);
}
return response;
}
CacheInterceptor.intercept()方法也挺長的,首先從cache緩存中獲取一個匹配的Response報文并賦給cacheCandidate變量,cache是一個InternalCache對象,里面持有DiskLruCache這個對象,以文件流的形式存儲Response報文,采用LRU原則管理這些緩存;接著使用CacheStrategy.Factory工廠類生成一個緩存策略類CacheStrategy,通過該類拿到兩個關(guān)鍵變量networkRequest和cacheResponse,這里針對cacheCandidate、networkRequest和cacheResponse這三個變量的賦值情況依次進(jìn)行了以下處理:
-
cacheCandidate不為空,cacheResponse為空,說明緩存過期,將cacheCandidate從cache中清除; -
networkRequest和cacheResponse同時為空,說明Request要求只使用緩存,而緩存并不存在或者已經(jīng)失效,直接返回504的錯誤報文,請求結(jié)束; -
networkRequest為空,說明cacheResponse不為空,命中緩存,直接返回cacheResponse報文; - 未命中緩存,開啟網(wǎng)絡(luò)請求,繼續(xù)執(zhí)行下一個Interceptor攔截器。
分析完Request,對請求回來的Response報文處理就很簡單了,就是針對Response報文情況決定是否使用緩存特性。
OkHttp3的Cache相關(guān)策略可參考RFC 2616, 14.9
當(dāng)緩存未命中時候OkHttp3就開始執(zhí)行真正的網(wǎng)絡(luò)請求,CacheInterceptor的下一個就是ConnectInterceptor攔截器。
// 所在類 okhttp3.internal.connection.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");
HttpStream httpStream = streamAllocation.newStream(client, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpStream, connection);
}
ConnectInterceptor做的事情很簡單,先獲取了在RetryAndFollowUpInterceptor中創(chuàng)建的StreamAllocation對象,接著執(zhí)行streamAllocation.newStream()打開一個物理連接并返回一個HttpStream的對象,HttpStream在前文提到了是網(wǎng)絡(luò)協(xié)議流(HTTP/1.1、HTTP/2和SPDY)的具體實(shí)現(xiàn)。這時候調(diào)用realChain.proceed()方法的時候,四個參數(shù)均不為空,這是集齊了所有的"龍珠"召喚最終的"神龍"CallServerInterceptor攔截器了。
在ConnectInterceptor的下一個攔截器并非絕對是CallServerInterceptor,如果有自定義NetInterceptors則會被優(yōu)先執(zhí)行,不過絕大部分情況下CallServerInterceptor在最后也是會被調(diào)用的。
// 所在類 okhttp3.internal.http.CallServerInterceptor
@Override public Response intercept(Chain chain) throws IOException {
HttpStream httpStream = ((RealInterceptorChain) chain).httpStream();
StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();
Request request = chain.request();
long sentRequestMillis = System.currentTimeMillis();
httpStream.writeRequestHeaders(request);
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
Sink requestBodyOut = httpStream.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
httpStream.finishRequest();
Response response = httpStream.readResponseHeaders()
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
if (!forWebSocket || response.code() != 101) {
response = response.newBuilder()
.body(httpStream.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
int code = response.code();
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攔截器里先調(diào)用httpStream協(xié)議流對象寫入Request的Header部分,接著寫入Body部分,這樣就完成了Request的請求,從httpStream里回讀Response報文,并根據(jù)情況讀取Response的Body部分,當(dāng)Response響應(yīng)報文的頭信息中Connection字段為close時,將streamAllocation設(shè)置成noNewStreams狀態(tài),標(biāo)識其當(dāng)前Connection對象不再被復(fù)用,將在流請求結(jié)束之后被回收掉。
總結(jié)
通過三四天對OkHttp3源碼的閱讀,佩服框架設(shè)計的巧妙,不光在于類的封裝上,里面對設(shè)計模式的實(shí)踐也挺好的。其中Prototype原型模式和Builder建造者模式被廣泛使用,關(guān)于Interceptor的概念使得全新設(shè)計一個私有請求協(xié)議不無可能,而okio對于流的封裝也是很巧妙的。推薦好好學(xué)習(xí)下。