一、概述
OkHttp 對(duì)于 Android 開(kāi)發(fā)人員來(lái)說(shuō)想必是人盡皆知的一個(gè)網(wǎng)絡(luò)請(qǐng)求框架了次舌,問(wèn)世之初就火爆了 Android 開(kāi)發(fā)圈,其優(yōu)異的設(shè)計(jì)更是讓不少技術(shù)大佬贊不絕口荆责,由其衍生的各種基于 OkHttp 的網(wǎng)絡(luò)框架也是層出不窮睹逃,同時(shí)各種對(duì)于 OkHttp 源碼分析文章也是數(shù)不勝數(shù),更是成為了面試常問(wèn)的問(wèn)題早直,可以說(shuō)市面上大多 App 都在使用著這套框架寥假。而且 Android 自 6.0 開(kāi)始也將內(nèi)部默認(rèn)的 HttpUrlConnection 換為了 OkHttp。更是確立了 OkHttp 在 Android 開(kāi)發(fā)生態(tài)中的地位霞扬。
對(duì)于每天都在使用的框架糕韧,如果只停留在使用而不了解它基本的工作原理,顯然是只見(jiàn)樹(shù)木不見(jiàn)森林的做法喻圃,也不利于自我提高萤彩,而互聯(lián)網(wǎng)的包容和開(kāi)放,更是讓我們可以輕松的看到這種優(yōu)秀的源碼斧拍,了解大神的思維以及設(shè)計(jì)一個(gè)優(yōu)秀框架的思想雀扶。畢竟站在巨人的肩膀上才能看到更遠(yuǎn)處的風(fēng)景。
本文將會(huì)從使用著手饮焦,不會(huì)太糾纏于細(xì)枝末節(jié)怕吴,著重分析 OkHttp 從發(fā)起請(qǐng)求到獲取響應(yīng)的總體脈絡(luò)流程,代碼會(huì)進(jìn)行適當(dāng)精簡(jiǎn)县踢,只展示核心邏輯
二转绷、一個(gè)簡(jiǎn)單的請(qǐng)求
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://raw.github.com/square/okhttp/master/README.md")
.build();
Response response = client.newCall(request).execute()
System.out.println("OKHTTP : " + response.body().string());
上述代碼是簡(jiǎn)化后的官方 GET 同步請(qǐng)求的例子,也是我們?nèi)粘J褂玫姆绞脚鹌。煌氖钱惒秸{(diào)用的是 enqueue 而已议经,這么幾行代碼就發(fā)起了一個(gè)網(wǎng)絡(luò)請(qǐng)求,我是著實(shí)有點(diǎn)好奇 OkHttp 內(nèi)部到底做了什么處理谴返,老規(guī)矩煞肾,讓我們先看下 OkHttp 的流程圖
通過(guò)上圖我們可以看到整個(gè)流程的關(guān)鍵節(jié)點(diǎn)由以下幾個(gè)核心類(lèi)組成
OkHttpClient
顧名思義 OkHttpClient 為我們創(chuàng)建一個(gè)請(qǐng)求的客戶(hù)端,內(nèi)部對(duì) OkHttp 各項(xiàng)參數(shù)進(jìn)行了默認(rèn)初始化嗓袱,同時(shí)對(duì) OkHttp 的處理邏輯進(jìn)行統(tǒng)一管理籍救,它通過(guò) Builder 構(gòu)造器生成,而根據(jù) OkHttp 的官方建議渠抹,對(duì)于使用來(lái)說(shuō) OkHttpClient 在全局的實(shí)例一個(gè)就夠了蝙昙,無(wú)須創(chuàng)造多個(gè)造成資源浪費(fèi)
Request 和 Response
Request 對(duì)象是我們要發(fā)送的具體請(qǐng)求闪萄,通過(guò)源碼我們可以發(fā)現(xiàn),它也是通過(guò) Builder 進(jìn)行構(gòu)建奇颠,內(nèi)部包含了符合 Http 協(xié)議的必要配置參數(shù)
// Request 的構(gòu)造函數(shù)
Request(Builder builder) {
this.url = builder.url; // 請(qǐng)求的 url 地址
this.method = builder.method; // 請(qǐng)求方法 get/pos
this.headers = builder.headers.build(); //請(qǐng)求頭
this.body = builder.body; //請(qǐng)求體
this.tags = Util.immutableMap(builder.tags); //請(qǐng)求的 tag 標(biāo)記用于取消請(qǐng)求
}
Response 則是請(qǐng)求的結(jié)果信息败去,通過(guò)源碼我們可以看到與 Request 如出一轍,也是通過(guò) Builder 進(jìn)行構(gòu)建
//Response 構(gòu)造函數(shù)烈拒,省略部分代碼
Response(Builder builder) {
this.request = builder.request;//發(fā)起的請(qǐng)求
this.protocol = builder.protocol;//協(xié)議
this.code = builder.code;// 響應(yīng)碼
this.message = builder.message; //響應(yīng)信息
this.headers = builder.headers.build(); //響應(yīng)頭
this.body = builder.body //響應(yīng)body
}
RealCall
RealCall 正如它的名字一樣圆裕,是真正發(fā)起實(shí)際請(qǐng)求并處理請(qǐng)求內(nèi)部一系列邏輯的類(lèi),所有的請(qǐng)求都會(huì)經(jīng)過(guò)它的處理和調(diào)度荆几,所以接下來(lái)通過(guò)部分代碼讓我們看看 RealCall 到底做了些什么
RealCall 的實(shí)例化過(guò)程
//這是OkHttp獲取請(qǐng)求結(jié)果的一行代碼
//這里 OkHttpCient 將構(gòu)建好的 request 放入 newCall
//函數(shù)中并直接執(zhí)行
Response response = client.newCall(request).execute()
//OkHttpClient 的 newCall 函數(shù)主要作用是將當(dāng)前
//OkHttpClient 和 Request 的引用傳給 RealCall
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
// RealCall 類(lèi)的靜態(tài) newRealCall 方法,這里是真正創(chuàng)建
//RealCall 實(shí)例對(duì)象的例子
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.transmitter = new Transmitter(client, call);
return call;
}
getResponseWithInterceptorChain 方法
創(chuàng)建完 RealCall 的實(shí)例后吓妆,我們就可以進(jìn)行 execute 和 enqueue 操作,也就是同步可異步的請(qǐng)求伴郁,但如果我們繼續(xù)跟進(jìn)代碼就會(huì)發(fā)現(xiàn)無(wú)論是同步還是異步最終都會(huì)調(diào)用 getResponseWithInterceptorChain() 方法來(lái)獲取 response耿战,而區(qū)別就是同步方法是直接調(diào)用,異步方法則是通過(guò)封裝了一個(gè) AsyncCall 線(xiàn)程來(lái)調(diào)用焊傅。所以我們可以基本確定整個(gè) OkHttp 的核心請(qǐng)求邏輯應(yīng)該就隱藏在這個(gè)方法當(dāng)中,讓我們來(lái)看下這方法的源碼
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
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);
}
}
}
getResponseWithInterceptorChain 方法的內(nèi)部邏輯還是很簡(jiǎn)單的狈涮,主要是構(gòu)建了一個(gè)攔截器的集合狐胎,將所有的攔截器按照順序依次添加,然后創(chuàng)建了一個(gè) RealInterceptorChain 實(shí)例歌馍,通過(guò) proceed 方法按照順序執(zhí)行各個(gè)攔截器邏輯握巢,跟進(jìn)源碼繼續(xù)看看
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
//省略若干代碼...
// 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);
//省略若干代碼...
return response;
}
proceed 方法內(nèi)部做的事情也并不復(fù)雜,總結(jié)起來(lái)就是這個(gè)方法會(huì)根據(jù) interceptors.size 來(lái)進(jìn)行一個(gè)循環(huán)的攔截器調(diào)用(具體從哪里循環(huán)后面會(huì)講到)松却,在執(zhí)行攔截器之前會(huì)創(chuàng)建一下攔截器的 RealInterceptorChain 實(shí)例暴浦,同時(shí) index 也會(huì)遞增,然后執(zhí)行當(dāng)前的攔截器鏈條并將下一個(gè)鏈條傳入晓锻,整個(gè)責(zé)任鏈就是以這種內(nèi)嵌的方式層層執(zhí)行歌焦,直到所有鏈條上的攔截器執(zhí)行完畢為止
其實(shí)從上面的代碼我們也可以清晰的看出 OkHttp 與我們以往的網(wǎng)絡(luò)請(qǐng)求框架的明顯區(qū)別,以往的網(wǎng)絡(luò)請(qǐng)求框架其請(qǐng)求過(guò)程基本都是封裝好的很難做出改變砚哆,而 OkHttp 則將請(qǐng)求的邏輯以攔截器的方式切成一個(gè)個(gè)獨(dú)立的執(zhí)行單元独撇,然后通過(guò)責(zé)任鏈的設(shè)計(jì)模式將其串聯(lián)成一個(gè)整體,最終通過(guò)層層的處理獲取到我們想要的響應(yīng)躁锁。而且攔截器對(duì)外是開(kāi)放的纷铣,這使得開(kāi)發(fā)者可以在請(qǐng)求的過(guò)程中根據(jù)需求定制屬于自己的攔截器,可以說(shuō)這也是 OkHttp 為什么備受推崇的原因之一战转。
二搜立、OkHttp 的攔截器
從上面的分析中我們可以看到 OkHttp 默認(rèn)為我們提供了五個(gè)攔截器如下
- RetryAndFollowUpInterceptor
- BridgeInterceptor
- CacheInterceptor
- ConnectInterceptor
- CallServerInterceptor
接下來(lái)我們依次來(lái)分析一下各個(gè)攔截器的內(nèi)部邏輯
2.1、RetryAndFollowUpInterceptor
RetryAndFollowUpInterceptor 主要是用來(lái)處理連接失敗過(guò)程中的常見(jiàn)的異常和重定向問(wèn)題槐秧,讓我們看下它的核心代碼
Request request = chain.request();
//下一個(gè)待執(zhí)行的攔截器鏈條
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();
int followUpCount = 0;
Response priorResponse = null;
//這里開(kāi)啟了一個(gè)死循環(huán)啄踊,也是從這里不斷的層層執(zhí)行責(zé)任鏈條上的每一個(gè)
//攔截器
while (true) {
//檢查連接的準(zhǔn)備狀態(tài)寸潦,如果是重復(fù)請(qǐng)求或正在請(qǐng)求則終止連接
transmitter.prepareToConnect(request);
//省略部分代碼...
Response response;
boolean success = false;
try {
//執(zhí)行下一個(gè)攔截器
response = realChain.proceed(request, transmitter, null);
success = true;
} catch (RouteException e) {
//當(dāng)請(qǐng)求發(fā)生異常時(shí)會(huì)嘗試重新連接.
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
continue;
} catch (IOException e) {
// //當(dāng)請(qǐng)求發(fā)生異常時(shí)會(huì)嘗試重新連接.
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;
//如果沒(méi)有發(fā)生異常則對(duì)響應(yīng)的 code 進(jìn)行判斷社痛,如果是 30X 則構(gòu)建新的
//request 進(jìn)行重定向操作见转,感興趣的可以看下源碼,不是很復(fù)雜
Request followUp = followUpRequest(response, route);
//省略若干代碼...
}
代碼中的注釋解釋的很清晰 蒜哀,這里在著重說(shuō)一下 recover 這個(gè)方法斩箫,也是判斷是否可以重試的重要條件
private boolean recover(IOException e, Transmitter transmitter,
boolean requestSendStarted, Request userRequest) {
// client 設(shè)置為禁止重試
if (!client.retryOnConnectionFailure()) return false;
// 請(qǐng)求的 body 已經(jīng)發(fā)出,則不在進(jìn)行請(qǐng)求
if (requestSendStarted && requestIsOneShot(e, userRequest)) return false;
// 致命的異常撵儿,比如 ProtocolException乘客、SSLHandshakeException 等
if (!isRecoverable(e, requestSendStarted)) return false;
// 沒(méi)有更多的 Route 嘗試
if (!transmitter.canRetry()) return false;
// For failure recovery, use the same route selector with a new connection.
return true;
}
可以看到重試的邏輯共有四個(gè)規(guī)則,如果都未通過(guò)那么重試就算失敗了淀歇,并且會(huì)在 finally 中釋放所有資源
2.2易核、BridgeInterceptor
這個(gè)攔截器的內(nèi)部邏輯其實(shí)很簡(jiǎn)單,它的作用就是如它的名字一般浪默,負(fù)責(zé)建立一個(gè)請(qǐng)求和響應(yīng)的橋梁牡直, 比如在請(qǐng)求的設(shè)置各種常用的 header 例如 Content-Type 、cookie纳决、User-Agent碰逸、 Gzip 壓縮等等,獲取響應(yīng)時(shí)配置響應(yīng)頭以及 Gzip 的解壓縮操作阔加,總體就是在請(qǐng)求之前和之后做的各種配置和轉(zhuǎn)換操作饵史,代碼很簡(jiǎn)單就不貼了
2.3、CacheInterceptor
OkHttp 的緩存攔截器的邏輯其實(shí)也是萬(wàn)變不離其宗胜榔,總體的思路并沒(méi)有和其它的框架有什么大的不同胳喷,也就是先從緩存中拿,緩存沒(méi)有在從網(wǎng)絡(luò)中拿夭织,只是 OkHttp 內(nèi)部對(duì)緩存的各種邏輯做了很完善的處理吭露。 但這里要注意的是 OKHttp 默認(rèn)是不支持緩存的,默認(rèn)提供的緩存功能為 Cache 類(lèi)摔癣,如果需要緩存則需要在創(chuàng)建 OkHttpClient 的時(shí)候手動(dòng)設(shè)置奴饮。
這里稍微提一下這個(gè)OkHttp提供的 Cache 類(lèi),我們知道凡是涉及到緩存功能的無(wú)非就是在設(shè)置的存儲(chǔ)范圍內(nèi)進(jìn)行增刪改查择浊,OkHttp 在這點(diǎn)上與其它的緩存策略并未有什么不同戴卜,不信我們看下 Cache 類(lèi)的大致結(jié)構(gòu)
可以看到幾個(gè)很關(guān)鍵的方法 get、put琢岩、remove投剥、update、initialize担孔,前四個(gè)不多說(shuō)了 initialize 這個(gè)方法主要是做緩存之前的初始化操作江锨,比如文件創(chuàng)建吃警,刪除以及異常的處理,那么這些增刪改查的主要操作其實(shí)都是通過(guò) DiskLruCache 類(lèi)完成啄育,也可以說(shuō)整個(gè) Cache 類(lèi)其實(shí)就是對(duì)于 DiskLruCahe 操作的封裝酌心,而 DiskLruCache 則是對(duì) okio包下的 BufferSource 和 BufferedSink 做了封裝簡(jiǎn)化對(duì)文件的讀寫(xiě)操作√敉悖總體流程就是這些安券,具體的細(xì)節(jié)感興趣的讀者可以根據(jù)這幾個(gè)關(guān)鍵方法來(lái)深入了解 OkHttp 的 Cache 邏輯,這里就不贅述了氓英。
啰嗦了這么多下面我們進(jìn)入正題
@Override
public Response intercept(Chain chain) throws IOException {
//判斷是否配置了緩存侯勉,如果有則獲取緩存
Response cacheCandidate = cache != null? cache.get(chain.request()) : null;
long now = System.currentTimeMillis();
//獲取緩存的策略,策略的判斷條件為 當(dāng)前時(shí)間铝阐,request 和 response
//其實(shí)主要就是解析 response 和 request 各種 header 信息以此來(lái)獲取緩存策略
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;//網(wǎng)絡(luò)請(qǐng)求
Response cacheResponse = strategy.cacheResponse;//緩存響應(yīng)
if (cache != null) { //如果緩存不為空則直接采用上面的策略
cache.trackResponse(strategy);
}
//省略部分代碼...
//網(wǎng)絡(luò)請(qǐng)求和緩存都為空返回構(gòu)建的 504 response
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();
}
// 上面的條件為 false 且網(wǎng)絡(luò)請(qǐng)求為 null 直接返回緩存
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
//緩存沒(méi)有數(shù)據(jù)址貌,則執(zhí)行下一個(gè)責(zé)任鏈
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());
}
}
// 當(dāng)緩存不為空,但網(wǎng)絡(luò)返回的 response code = 304
// 直接返回緩存的 response
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());
}
}
//構(gòu)建網(wǎng)絡(luò)請(qǐng)求的 response
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
//如果緩存對(duì)象不為空則說(shuō)明 OkHttpClient 創(chuàng)建的時(shí)候
//配置了緩存對(duì)象徘键,那么對(duì)于請(qǐng)求的 response 進(jìn)行緩存
if (cache != null) {
//判斷是否可以緩存,主要是判斷各種 code 是否滿(mǎn)足緩存條件等等
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
//寫(xiě)入磁盤(pán)
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
//判斷請(qǐng)求方法是否為 GET 练对,非 GET 請(qǐng)求緩存無(wú)效并刪除
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
通過(guò)對(duì)代碼的逐行分析,我們發(fā)現(xiàn) OkHttp 在緩存的處理上是非常完善的啊鸭,基本覆蓋了常見(jiàn)的緩存問(wèn)題锹淌,整體的邏輯也不是那么復(fù)雜,但到這里你會(huì)發(fā)現(xiàn)我們依然在應(yīng)用層轉(zhuǎn)悠赠制,具體怎么進(jìn)行網(wǎng)絡(luò)請(qǐng)求的,我們依舊是一無(wú)所知挟憔,所以接下來(lái)我們來(lái)看看剩下的兩個(gè)攔截器是怎么完成這個(gè)過(guò)程的钟些。
2.4、ConnectInterceptor
ConnectInterceptor 可以說(shuō)是整個(gè) OkHttp 核心中的核心了绊谭,眾所周知當(dāng)我們通過(guò)一個(gè) url 發(fā)起一個(gè)請(qǐng)求時(shí)會(huì)先經(jīng)過(guò) DNS 進(jìn)行域名解析獲取實(shí)際的 IP 地址政恍,然后根據(jù) IP 地址和目標(biāo)服務(wù)器建立連接,在經(jīng)過(guò)三次握手后連接成功达传,我們就可以發(fā)送或接收消息了篙耗。 而 ConnectInterceptor 做的就是這些事,它將整個(gè)連接過(guò)程高度封裝宪赶,而且對(duì)連接做了復(fù)用處理宗弯,request 與 response 更是全部使用 okio 包下的 I/O 流進(jìn)行讀寫(xiě)操作,大大提升了請(qǐng)求效率搂妻。 所以廢話(huà)不多說(shuō)蒙保,上代碼~
@Override
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
Transmitter transmitter = realChain.transmitter();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//這里開(kāi)始建立 Socket 連接,而且如果是非 GET 請(qǐng)求則要做更多的安全檢查
Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
//執(zhí)行下一個(gè)攔截器
return realChain.proceed(request, transmitter, exchange);
}
可以看到 ConnectInterceptor 的代碼并不多欲主,這里我們主要關(guān)注的就是 Transmitter 對(duì)象中的 newExchange 方法
/** Returns a new exchange to carry a new request and response. */
Exchange newExchange(Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
synchronized (connectionPool) {
//出現(xiàn)了異常連接被釋放了
if (noMoreExchanges) {
throw new IllegalStateException("released");
}
//有別的請(qǐng)求未關(guān)閉則無(wú)法創(chuàng)建新的請(qǐng)求
if (exchange != null) {
throw new IllegalStateException("cannot make a new request because the previous response "
+ "is still open: please call response.close()");
}
}
//建立連接
ExchangeCodec codec = exchangeFinder.find(client, chain, doExtensiveHealthChecks);
Exchange result = new Exchange(this, call, eventListener, exchangeFinder, codec);
synchronized (connectionPool) {
this.exchange = result;
this.exchangeRequestDone = false;
this.exchangeResponseDone = false;
return result;
}
}
代碼不多但很關(guān)鍵邓厕,首先 ExchangeCodec 是一個(gè)接口逝嚎,它的實(shí)現(xiàn)類(lèi)有兩個(gè),一個(gè)是Http1ExchangeCodec详恼,一個(gè)是Http2Exchangecodec,相信從這里你已經(jīng)明白了补君,沒(méi)錯(cuò),一個(gè)對(duì)應(yīng)的是 Http1 協(xié)議的連接昧互,一個(gè)則是 Http2 協(xié)議挽铁。而不同的 HttpCodec 代表不同的連接邏輯,雖然它倆的連接邏輯不同硅堆,但其實(shí)干的事情都一樣屿储,就是對(duì) Socket I/O 操作做了封裝,然后就是 ExchangeFinder 對(duì)象, 這個(gè)對(duì)象內(nèi)部實(shí)現(xiàn)了連接的復(fù)用機(jī)制渐逃,包括連接的重試等等够掠,不過(guò)到這里你可能會(huì)有點(diǎn)疑惑好像沒(méi)看到在哪創(chuàng)建這個(gè)類(lèi)的實(shí)例啊,提示一下茄菊,到 RetryAndFollowUpInterceptor 中找到 prepareToConnect 方法進(jìn)入后你就明白了疯潭。至于 Exchange 則是對(duì)連接的包裝類(lèi)
了解了這些類(lèi)的基本作用后那么 find 方法的做的事情我們也就基本清楚了,就是根據(jù)我們的請(qǐng)求建立 http1/http2 的的連接并返回對(duì)應(yīng)的 ExchangeCodec 對(duì)象面殖,我們進(jìn)去看看
final class ExchangeFinder {
public ExchangeCodec find(
OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
//省略若干代碼...
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
return resultConnection.newCodec(client, chain);
//省略若干代碼...
}
}
可以看到內(nèi)部調(diào)用 findHealthyConnection 方法竖哩,并返回一個(gè) RealConnection 對(duì)象,并通過(guò) RealConnection 對(duì)象的 newCodec 方法創(chuàng)建對(duì)應(yīng)協(xié)議的 ExchangeCodec 對(duì)象脊僚。
這里著重說(shuō)一下 RealConnection 這個(gè)對(duì)象相叁,顧名思義這個(gè)對(duì)象是真正處理 Socket 連接的對(duì)象, RealConnection 處理的事情非常之多辽幌,包含了 socket 連接的所有細(xì)節(jié)例如 TCP + TLS 握手增淹、Http/http2 連接的處理等等,并通過(guò) RealConnectionPool 實(shí)現(xiàn)了連接的復(fù)用邏輯乌企,RealConnectionPool 內(nèi)部維護(hù)著一個(gè)線(xiàn)程池和一個(gè) RealConnection 的隊(duì)列虑润,這個(gè)線(xiàn)程池會(huì)在后臺(tái)一直運(yùn)行,負(fù)責(zé)清理過(guò)期的連接加酵、執(zhí)行新的連接或已有的連接拳喻。
繼續(xù)跟進(jìn) findHealthyConnection 方法
final class ExchangeFinder {
/**
* Finds a connection and returns it if it is healthy. If it is unhealthy the process is repeated
* until a healthy connection is found.
*/
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
boolean doExtensiveHealthChecks) throws IOException {
while (true) {
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
pingIntervalMillis, connectionRetryEnabled);
//省略若干代碼...
return candidate;
}
}
}
注釋我故意沒(méi)有刪,因?yàn)樗呀?jīng)表明了這個(gè)方法的作用猪腕,注釋的意思是說(shuō)冗澈,找到一個(gè)安全的連接,如果不安全就一直在循環(huán)中找码撰,直到找到為止渗柿,不過(guò)說(shuō)雖然是這么說(shuō),當(dāng)然不可能會(huì)無(wú)限找的,別忘了我們是有連接時(shí)超時(shí)限制的朵栖。
繼續(xù)跟進(jìn) findConnection 方法
final class ExchangeFinder {
/**
* 返回一個(gè)連接的流颊亮,如果這個(gè)連接在連接池中存在則返回,否則創(chuàng)建
* 一個(gè)新的連接返回
*/
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
RealConnection result = null;
Route selectedRoute = null;
RealConnection releasedConnection;
Socket toClose;
synchronized (connectionPool) {
//如果已經(jīng)建立了連接陨溅,但中間卻出現(xiàn)了異常導(dǎo)致連接出現(xiàn)問(wèn)題
//那么釋放連接或創(chuàng)建新的連接
releasedConnection = transmitter.connection;
toClose = transmitter.connection != null && transmitter.connection.noNewExchanges
? transmitter.releaseConnectionNoEvents()
: null;
if (transmitter.connection != null) {
//如果已有連接那么直接使用
result = transmitter.connection;
releasedConnection = null;
}
//沒(méi)有可用的連接
if (result == null) {
//嘗試從連接池中獲取連接進(jìn)行復(fù)用
if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, null, false)) {
foundPooledConnection = true;
result = transmitter.connection;
} else if (nextRouteToTry != null) {
//如果連接池中也沒(méi)有那么用其它的 route 重試
selectedRoute = nextRouteToTry;
nextRouteToTry = null;
} else if (retryCurrentRoute()) {
//如果設(shè)置了重試當(dāng)前 route 那么根據(jù)重試的 route 連接
selectedRoute = transmitter.connection.route();
}
}
}
if (result != null) {
//如果連接池有可用的連接终惑,直接使用
return result;
}
// 如果上面的條件都沒(méi)有獲取到連接,那么說(shuō)明我們要?jiǎng)?chuàng)建一個(gè)新的連接
//并通過(guò) DNS 解析獲取要連接的 route
boolean newRouteSelection = false;
if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
newRouteSelection = true;
routeSelection = routeSelector.next();
}
List<Route> routes = null;
synchronized (connectionPool) {
if (transmitter.isCanceled()) throw new IOException("Canceled");
if (newRouteSelection) {
// 如果有新的 route 那么獲取所有 route
routes = routeSelection.getAll();
//在此查看連接池中是否有可用的連接如果有直接使用
if (connectionPool.transmitterAcquirePooledConnection(
address, transmitter, routes, false)) {
foundPooledConnection = true;
result = transmitter.connection;
}
}
//連接池中沒(méi)有可用連接门扇,且沒(méi)有可選擇的 route
//通過(guò) next 方法用 DNS 解析出新的 IP 地址用于接下來(lái)的連接
if (!foundPooledConnection) {
if (selectedRoute == null) {
selectedRoute = routeSelection.next();
}
// 通過(guò)上面獲取的 route 創(chuàng)建一個(gè)新的 RealConnection 對(duì)象
result = new RealConnection(connectionPool, selectedRoute);
connectingConnection = result;
}
}
// 做 TCP+TLS 握手雹有,注意這是一個(gè)阻塞操作
//到這一步執(zhí)行完整個(gè)連接算是真正的建立了
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
connectionPool.routeDatabase.connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
connectingConnection = null;
// 這里會(huì)再次進(jìn)行一次判斷,查看連接池中是否有可用的連接
if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, routes, true)) {
//如果有那么關(guān)閉連接池中的連接臼寄,返回我們新創(chuàng)建的連接
result.noNewExchanges = true;
socket = result.socket();
result = transmitter.connection;
//將上面獲取的新的 route 標(biāo)記位下次重試的 route
nextRouteToTry = selectedRoute;
} else {
//將新創(chuàng)建的連接放入連接池方便復(fù)用
connectionPool.put(result);
transmitter.acquireConnectionNoEvents(result);
}
}
return result;
}
}
上面的代碼就是 OkHttp 建立連接的整個(gè)過(guò)程霸奕,說(shuō)實(shí)話(huà)整體的代碼是有點(diǎn)復(fù)雜的,而且各種層級(jí)關(guān)系還不少吉拳,如果不耐心閱讀很容易跟錯(cuò)质帅,這里我也是讀了很久才逐漸縷清楚整個(gè)連接過(guò)程。所以讓我們做個(gè)復(fù)盤(pán)總結(jié)一下留攒,首先 ConnectInterceptor 的主要作用就是建立 socket 連接并將其包裝成 Exchange 對(duì)象供下一個(gè)攔截器使用煤惩, 而 Exchange 其實(shí)就是 Http1ExchangeCodec 和 Http2ExchangeCodec 的包裝類(lèi),負(fù)責(zé)對(duì) request 和 response 的 header body 進(jìn)行流的讀寫(xiě)操作炼邀。建立連接的整個(gè)過(guò)程如下
- 調(diào)用 transmitter的.newExchange 方法獲取 Exchange 對(duì)象
- newExchange 方法調(diào)用 ExchangeFinder.find 方法獲取 ExchangeCodec 對(duì)象
- find 方法 調(diào)用 ExchangeFinder 的 findHealthyConnection 方法獲取 RealConnection 對(duì)象并調(diào)用該對(duì)象的 newCodeC 方法創(chuàng)建一個(gè) ExchangeCodec 對(duì)象返回
- findHealthyConnection 方法再調(diào)用 ExchangeFinder 的 findConnection 方法獲取 RealConnection 對(duì)象
- findConnection 方法中通過(guò) DNS 解析 IP地址魄揉,創(chuàng)建 RealConnection 對(duì)象并建立連接后返回 RealConnection 實(shí)例,同時(shí)實(shí)現(xiàn)了連接的復(fù)用邏輯
經(jīng)過(guò)上面的一系列調(diào)用 ConnectInterceptor 就完成了它的光榮使命拭宁,這里在提一個(gè)細(xì)節(jié)洛退,讓我們回顧一下以往的代碼
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
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())
}
//省略...
}
其實(shí)剛開(kāi)始讀到這里的時(shí)候我是有點(diǎn)疑惑的,interceptors 和 networkInterceptors 有什么區(qū)別杰标? 而經(jīng)過(guò)了上面的分析相信你會(huì)恍然大悟不狮,很簡(jiǎn)單, interceptors 是連接之前的處理,而 ConnectInterceptor 執(zhí)行完之后網(wǎng)絡(luò)連接已經(jīng)建立了在旱,那么后續(xù)的攔截只能是對(duì)連接后的過(guò)程進(jìn)行處理,所以才會(huì)叫 networkInterceptors
三推掸、結(jié)語(yǔ)
注釋里基本對(duì)每個(gè)方法都做了詳細(xì)說(shuō)明桶蝎,相信到這里我們腦海中對(duì) OkHttp 的連接流程的總體脈絡(luò)應(yīng)該有了一個(gè)較為清晰的認(rèn)識(shí),但還沒(méi)完谅畅,Socket 在哪連接的登渣, DNS 解析好像也沒(méi)看到, ExchangeCodec實(shí)例對(duì)象是怎么創(chuàng)建的毡泻?這些在上述的代碼中我們都沒(méi)看到胜茧,所以下面我還會(huì)挨個(gè)對(duì)上述的問(wèn)題一個(gè)一個(gè)解密,限于篇幅,想繼續(xù)了解可以閱讀 OkHttp 源碼深入分析(二)