OkHttp已經(jīng)很出名仅偎,這里就不啰嗦了奶赠,直接進(jìn)入主題,以下代碼演示均使用Kotlin熙尉。
OkHttp版本:4.9.0
我們先從發(fā)起一個(gè)簡單的請求來說起联逻。
val url = "https://api.github.com/users/huangcv/repos"
val client = OkHttpClient()
val request = Request
.Builder()
.url(url)
.build()
client.newCall(request)
.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
println("Response status code:${response.code}")
}
})
結(jié)果如下:
一個(gè)簡單的請求就結(jié)束了。
接下來我們簡單的來分析下源碼检痰,先從最近的地方開始著手看源碼包归,就是
.enqueue()
這里,我們點(diǎn)擊去發(fā)現(xiàn)是一個(gè)接口铅歼,然后退出來找上一個(gè)節(jié)點(diǎn)的代碼公壤,就是.newCall()
這里,看創(chuàng)建了一個(gè)怎樣的Call
對象椎椰。
/** Prepares the [request] to be executed at some point in the future. */
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
我們在跟到RealCall
對象里面厦幅。
class RealCall(
val client: OkHttpClient,
/** The application's original request unadulterated by redirects or auth headers. */
val originalRequest: Request,
val forWebSocket: Boolean
) : Call {
......
}
構(gòu)造函數(shù)中第一個(gè)參數(shù),就是我們最開始創(chuàng)建的那個(gè)OkHttpClient
俭识,這個(gè)類就相當(dāng)于一個(gè)大總管慨削,一些通用的配置都在這里,比如讀寫超時(shí)時(shí)間套媚,設(shè)置代理缚态,緩存等都是在這個(gè)類中設(shè)置的。
第二個(gè)參數(shù):Request堤瘤,初始的請求玫芦,就是我們newCall
傳進(jìn)來的Request,而且看它的名字是originalRequest本辐,因?yàn)閺奈覀儼l(fā)起請求到最終將一個(gè)請求發(fā)送出去這個(gè)過程中Request會(huì)被包裝好幾層桥帆。
第三個(gè)從參數(shù):是否是WebSocket,默認(rèn)false慎皱,不是WebSocket請求老虫。
所以到這一步我們就清楚了,newCall
的作用就是創(chuàng)建一個(gè)RealCall
對象茫多,將OkHttpClient和Request傳遞進(jìn)去祈匙。
接下來我們來看下RealCall具體干了什么?
還記得我們第一次從 enqueue
函數(shù)點(diǎn)進(jìn)去的嗎,因?yàn)槔锩媸且粋€(gè)接口夺欲,所以我們才來到了newCall
跪帝,因?yàn)镽ealCall實(shí)現(xiàn)了Call這個(gè)接口,接下來我們就要看下RealCall實(shí)現(xiàn)了Call之后的enqueue
函數(shù)了些阅。
點(diǎn)進(jìn)去可以看到:
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
第一行檢查這個(gè)call是否被執(zhí)行過了
第二行:callStart()伞剑,我們點(diǎn)進(jìn)去看下;
private fun callStart() {
this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()")
eventListener.callStart(this)
}
第一行用于跟蹤錯(cuò)誤市埋,用于錯(cuò)誤分析黎泣。
第二行:eventListener.callStart(this),事件監(jiān)聽器的回調(diào)函數(shù)腰素。
這里沒有重要的信息聘裁,我們再回過去看第三行代碼:
client.dispatcher.enqueue(AsyncCall(responseCallback))
這句話是重點(diǎn),我們需要看client的dispatcher的enqueue函數(shù)干了什么弓千,并且這里還傳入了一個(gè)AsyncCall對象。
我們先看這個(gè)dispatcher是個(gè)什么東西献起?
點(diǎn)進(jìn)去:
@get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher
我們在點(diǎn)進(jìn)去洋访,到dispatcher類中看下:
/**
* Policy on when async requests are executed.
*
* Each dispatcher uses an [ExecutorService] to run calls internally. If you supply your own
* executor, it should be able to run [the configured maximum][maxRequests] number of calls
* concurrently.
*/
class Dispatcher constructor() {
......
}
我們看到類描述,dispatcher是用來做線程調(diào)度用的谴餐,因?yàn)橥粫r(shí)間我們可能發(fā)起了好幾個(gè)請求姻政,因?yàn)槊恳粋€(gè)請求都是一個(gè)線程,也就是說線程的執(zhí)行岂嗓,就代表著請求的開始汁展,所以實(shí)質(zhì)上dispatcher是對線程的調(diào)度管理,從他執(zhí)行過程中用的是ExecutorService就可以進(jìn)一步說明厌殉,dispatcher是一個(gè)多線程調(diào)度管理器食绿。
也就是說我們在分析dispatcher里面的代碼,基本上都是關(guān)于調(diào)度的公罕。
對于Dispatcher我們可以簡單來看下:
@get:Synchronized var maxRequests = 64
可以同時(shí)進(jìn)行請求的最大數(shù)量器紧,超過這個(gè)數(shù)量,就要進(jìn)行等待楼眷。
@get:Synchronized var maxRequestsPerHost = 5
每個(gè)主機(jī)下同時(shí)進(jìn)行請求的最大數(shù)量铲汪,如果通過主機(jī)超過這個(gè)數(shù)量,就需要等待罐柳。
@get:Synchronized
@get:JvmName("executorService") val executorService: ExecutorService
Diapatcher 底層對多線程的管理工具掌腰,使用的是Java自帶的多線程管理工具。
好了我們現(xiàn)在知道Dispatcher是干什么用的了张吉,之后我們再看下dispatcher.enqueue()干了什么齿梁?
點(diǎn)進(jìn)去看下:
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
可以看到最關(guān)鍵的就是兩句代碼,中間那一坨注釋已經(jīng)說的很明白了:就是針對同一個(gè)主機(jī)芦拿,檢查當(dāng)前正在執(zhí)行的請求數(shù)量士飒,是否超過最大請求數(shù)查邢,這個(gè)當(dāng)前正在請求的請求數(shù)對于每個(gè)請求都是共享的。
- readyAsyncCalls.add(AsyncCall)
- promoteAndExecute()
第一句是將我們傳進(jìn)來的AsyncCall對象添加到一個(gè)雙向隊(duì)列中酵幕,這里隊(duì)列中存放的都是準(zhǔn)備要執(zhí)行但是還沒有執(zhí)行的任務(wù)扰藕。
再看第二句:promoteAndExecute(),從字面意思來看執(zhí)行異步任務(wù)芳撒。
來看下具體的代碼:
/**
* Promotes eligible calls from [readyAsyncCalls] to [runningAsyncCalls] and runs them on the
* executor service. Must not be called with synchronization because executing calls can call
* into user code.
*
* @return true if the dispatcher is currently running calls.
*/
private fun promoteAndExecute(): Boolean {
this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
val i = readyAsyncCalls.iterator()
while (i.hasNext()) {
val asyncCall = i.next()
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
i.remove()
asyncCall.callsPerHost.incrementAndGet()
executableCalls.add(asyncCall)
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService)
}
return isRunning
}
代碼稍微多了一點(diǎn)邓深,其主要功能就是:
將上一步已經(jīng)準(zhǔn)備好但還沒有執(zhí)行的隊(duì)列中的請求都拿出來遍歷一遍,篩選出可以執(zhí)行的請求笔刹,然后用executorService來執(zhí)行這些所有可執(zhí)行的請求芥备。
我們具體來看下:
首先從AsyncCallDeque隊(duì)列中篩選出可執(zhí)行的請求,條件是沒有超過最大請求數(shù)(maxRequests
)舌菜,并且所請求的主機(jī)沒有超過最大請求數(shù)(maxRequestsPerHost
)萌壳,滿足這兩個(gè)條件,該請求即滿足可執(zhí)行請求的條件日月,然后加入到可執(zhí)行請求的集合中(executableCalls
)袱瓮,并且添加到正在執(zhí)行請求的隊(duì)列中(runningAsyncCalls
),用于后面做記錄用的爱咬。接下來就是拿著可執(zhí)行的請求的集合(executableCalls
)遍歷執(zhí)行這些請求并執(zhí)行尺借,怎么執(zhí)行呢,把這些請求都丟給ExecutorService來進(jìn)行執(zhí)行精拟,也就是調(diào)用AsyncCall
的executeOn函數(shù)燎斩。
點(diǎn)進(jìn)去看下:
/**
* Attempt to enqueue this async call on [executorService]. This will attempt to clean up
* if the executor has been shut down by reporting the call as failed.
*/
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
代碼并不多,我們來簡單分析下executeOn函數(shù)蜂绎。
關(guān)鍵代碼:executorService.execute(this)栅表,也就是說到這里線程發(fā)生了切換,不管之前的線程是什么荡碾,到這里統(tǒng)一都是后臺線程谨读。
至于后面的都是做一些掃尾工作,比如請求執(zhí)行的過程中是否報(bào)錯(cuò)坛吁,報(bào)錯(cuò)了進(jìn)行狀態(tài)的回調(diào)以及將該請求標(biāo)記為請求完成劳殖。這就不細(xì)聊了。
我們接下來看:executorService.execute(this)這句代碼拨脉,既然傳入的是this哆姻,那么它一定實(shí)現(xiàn)了Runnable接口,所以我們就找AsyncCall中的run函數(shù)玫膀。如下:
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
timeout.enter()
try {
val response = getResponseWithInterceptorChain()
signalledCallback = true
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
responseCallback.onFailure(this@RealCall, e)
}
} catch (t: Throwable) {
cancel()
if (!signalledCallback) {
val canceledException = IOException("canceled due to $t")
canceledException.addSuppressed(t)
responseCallback.onFailure(this@RealCall, canceledException)
}
throw t
} finally {
client.dispatcher.finished(this)
}
}
}
關(guān)鍵代碼:val response = getResponseWithInterceptorChain()矛缨,也就是說在這里我們可以直接拿到請求的響應(yīng)。接下來就是對響應(yīng)的一些處理,比如說:responseCallback.onResponse(this@RealCall, response)箕昭,回調(diào)請求響應(yīng)灵妨,即成功。
responseCallback.onFailure(this@RealCall, e)落竹,出現(xiàn)異常了回調(diào)失敗泌霍。
這里的responseCallback就是我們最開始從client.newCall(request).enqueue(Callback)
傳遞進(jìn)來的Callback。
接下來最終執(zhí)行
client.dispatcher.finished(this)函數(shù)述召,將該請求標(biāo)記為完成朱转。
經(jīng)過上述步驟之后也就意味著整個(gè)請求結(jié)束了。
結(jié)束了嗎积暖?顯然沒有藤为,因?yàn)槲覀冞€沒有看是如果拿到Response的,具體干活的地方還沒看呢夺刑。
接下來我們就要進(jìn)入到另一個(gè)全新的階段缅疟,根據(jù)請求獲取響應(yīng),這也是每一個(gè)請求最最最重要的地方性誉,同樣這樣是OkHttp最最最重要和硬核的部分窿吩。
在說getResponseWithInterceptorChain()
函數(shù)之前,我們有必要先了解下OkHttp的各種配置错览,這對后面理解getResponseWithInterceptorChain()
有很好的幫助。
OkHttp配置簡單介紹:
- dispatcher 多線程調(diào)度管理器煌往,前面我們已經(jīng)說過了倾哺,這里就不細(xì)說了。
- connectionPool 連接池刽脖,可以類比線程池羞海。連接池內(nèi)部存儲(chǔ)了一批的連接,當(dāng)需要用到連接的時(shí)候而不是先創(chuàng)建曲管,而是先看下池中是否有可重用的却邓,如果沒有再行創(chuàng)建,達(dá)到快速使用和創(chuàng)建院水,如果一個(gè)連接不再被使用的時(shí)候可以配置時(shí)間來進(jìn)行回收腊徙。池的作用就是批量管理對象,通過重用和自動(dòng)回收達(dá)到一種性能和資源占用的動(dòng)態(tài)平衡檬某。
- interceptors 是一個(gè)攔截器的集合撬腾。
- networkInterceptors 也是一個(gè)攔截器的集合。
- eventListenerFactory 用來生產(chǎn)EventListener的一個(gè)工廠恢恼,這個(gè)EventListener我們前面已經(jīng)說過了民傻,它是OkHttp各個(gè)過程和節(jié)點(diǎn)的事件監(jiān)聽器,通過設(shè)置EventListener我們可以監(jiān)聽到,請求過程中的各個(gè)節(jié)點(diǎn)的事件漓踢。
- retryOnConnectionFailure 當(dāng)連接失敗或者請求失敗時(shí)是否重連牵署,默認(rèn)是true,失敗重連喧半。
- authenticator 自動(dòng)進(jìn)行認(rèn)證修正的工具奴迅。
- followRedirects 是否跟蹤重定向,默認(rèn)為true薯酝,當(dāng)響應(yīng)碼為重定向301/302時(shí)半沽,如果為true時(shí),將會(huì)重新進(jìn)行重定向之后的請求吴菠,然后將結(jié)果返回者填,如果為false時(shí),將直接回調(diào)做葵,請求結(jié)束占哟。
- followSslRedirects 當(dāng)上面followRedirects為true的時(shí)候,當(dāng)出現(xiàn)重定向時(shí)發(fā)生協(xié)議切換時(shí)酿矢,是否繼續(xù)新的協(xié)議的請求榨乎,默認(rèn)為true。
- cookieJar 用來存儲(chǔ)Cookie瘫筐,默認(rèn)都是空實(shí)現(xiàn)蜜暑,如果需要保存Cookie,需要自己實(shí)現(xiàn)策肝。
- cache 緩存肛捍。
- dns 域名系統(tǒng)(Domain Name System),用于解析域名之众。最終使用的還
是Java的InetAddress.getAllByName()拙毫,這里不再細(xì)說了。 - proxy 代理棺禾。分為:直連缀蹄,HTTP以及Socket。
- proxySelector 代理選擇器膘婶。從一個(gè)列表中選擇可使用的代理缺前,如果沒有設(shè)置proxy,默認(rèn)為直連竣付。
- proxyAuthenticator 代理獨(dú)立的驗(yàn)證機(jī)制诡延,當(dāng)代理出現(xiàn)需要授權(quán)時(shí),通過這個(gè)來進(jìn)行給代理授權(quán)古胆。
- socketFactory HTTP本質(zhì)的連接就是建立Socket肆良,所建立的Socket就是從這里的工廠創(chuàng)建的筛璧。
- sslSocketFactoryOrNull 同上個(gè)同理,當(dāng)我們需要建立TLS/SSL加密連接時(shí)的Socket對象就是從這個(gè)sslSocketFactory來創(chuàng)建的惹恃。
- x509TrustManager HTTPS 建立連接時(shí)的證書驗(yàn)證器夭谤,X509是證書的一種格式,所有的HTTPS連接使用的都是X509格式的證書巫糙。
- connectionSpecs 連接規(guī)范/標(biāo)準(zhǔn)朗儒。就是HTTPS在建立連接時(shí),客戶端需要給服務(wù)器發(fā)送支持的TLS版本
以及支持的加密套件(對稱加密参淹,非對稱加密醉锄,哈希等)。 - protocols 支持的HTTP協(xié)議的版本號浙值,HTTP1.0恳不,HTTP1.1,SPYD3.1,HTTP2,H2_PRIOR_KNOWLEDGE。
- hostnameVerifier 主機(jī)名驗(yàn)證器开呐,屬于證書驗(yàn)證機(jī)制中的烟勋,我們客戶端驗(yàn)證證書主要是驗(yàn)證:1.證書的合法性,使用X509TrustMangaer來驗(yàn)證 2.是否是所訪問的網(wǎng)站的證書筐付,這里的第二步的驗(yàn)證就是使用HostnameVerifier來驗(yàn)證的卵惦。
- certificatePinner 證書強(qiáng)制驗(yàn)證的內(nèi)容,就是自己寫的代碼瓦戚,增強(qiáng)了驗(yàn)證的邏輯性沮尿。如果certificatePinner的驗(yàn)證不通過,即是證書驗(yàn)證是合法的较解,整個(gè)證書的驗(yàn)證流程也不會(huì)通過蛹找。
- certificateChainCleaner 這個(gè)也是證書驗(yàn)證機(jī)制中的,它的功能主要是操作X509TrustManager 驗(yàn)證證書的合法性哨坪。
- callTimeoutMillis 呼叫超時(shí),但是完整的呼叫是沒有超時(shí)的乍楚,但是呼叫中的連接当编,讀寫是有可能超時(shí)的。
- connectTimeoutMillis 連接超時(shí)徒溪。默認(rèn)10s忿偷。
- readTimeoutMillis 連接的讀操作超時(shí)時(shí)間,默認(rèn)10s臊泌。
- writeTimeoutMillis 連接的寫操作超時(shí)時(shí)間鲤桥,默認(rèn)10s。
- pingIntervalMillis 心跳間隔渠概,這個(gè)是針對WebSocket和HTTP2來說的茶凳,默認(rèn)不發(fā)送心跳嫂拴。
經(jīng)過以上部分的鋪墊,我們接下來就要進(jìn)入最最最硬核的部分了贮喧,有了以上部分的鋪墊筒狠,我們能更好的理解getResponseWithInterceptorChain
這個(gè)函數(shù)里面的東西。
不廢話箱沦,開始辩恼。
我們點(diǎn)進(jìn)去看下 getResponseWithInterceptorChain
這個(gè)函數(shù)的具體代碼如下:
internal fun getResponseWithInterceptorChain(): Response {
// Build a full stack of interceptors.
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
interceptors += CallServerInterceptor(forWebSocket)
val chain = RealInterceptorChain(
call = this,
interceptors = interceptors,
index = 0,
exchange = null,
request = originalRequest,
connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis
)
var calledNoMoreExchanges = false
try {
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
猛一看,我k谓形,代碼有點(diǎn)多啊灶伊,不過沒關(guān)系,老規(guī)矩寒跳,把代碼羅列一些聘萨,分分類,就會(huì)很清楚了冯袍。
第一部分匈挖,合并Interceptor,把默認(rèn)和自定的Interceptor都合并到一起康愤。
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
interceptors += CallServerInterceptor(forWebSocket)
第二部分儡循,構(gòu)建真正的InterceptorChain,對第一步的Interceptors進(jìn)行封裝和管理征冷,為接下來的鏈?zhǔn)焦ぷ髯鰷?zhǔn)備择膝。
val chain = RealInterceptorChain(
call = this,
interceptors = interceptors,
index = 0,
exchange = null,
request = originalRequest,
connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis
)
第三部分,執(zhí)行第一個(gè)Interceptor检激,讓整個(gè)InterceptorChain開始轉(zhuǎn)起來肴捉,當(dāng)執(zhí)行完第一步合并的所有Interceptor時(shí),整個(gè)流程就結(jié)束了叔收,此次的請求也結(jié)束了齿穗,就拿到了響應(yīng),這么神奇饺律?可是沒看到干什么扒砸场?那是因?yàn)榉庋b的太好了复濒。我們接下里仔細(xì)來看下這些代碼脖卖。
var calledNoMoreExchanges = false
try {
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
在看之前,我們下來了解下說明是鏈?zhǔn)焦ぷ髑删保哪P腿缦拢?/p>
一個(gè)接一個(gè)的從左往右執(zhí)行到最后一個(gè)畦木,然后再從右往左一個(gè)一個(gè)的執(zhí)行回來。就是每一個(gè)Interceptor都有前置工作砸泛,中置工作和后置工作十籍,而且最后一個(gè)節(jié)點(diǎn)有點(diǎn)特殊蛆封,因?yàn)樗鼪]有中置工作,所以導(dǎo)致它的前置和后置工作合在一起了妓雾,就只剩下一個(gè)工作娶吞。這個(gè)模型也很容易加入一些節(jié)點(diǎn),比如想在最前面加入一個(gè)初始化工作的節(jié)點(diǎn)械姻,將準(zhǔn)備好的Interceptor直接放到第一個(gè)位置妒蛇,然后開始執(zhí)行即可。
也就是說我們將第一部分的那一坨Interceptor看完楷拳,整個(gè)請求->響應(yīng)的過程也就都明白了绣夺。
好了接下來我們開始詳細(xì)看下第一部分的各個(gè)Interceptor。
因?yàn)?code>client.interceptors 和 client.networkInterceptors
都是用戶自定義的Interceptor欢揖,這里就沒必要看了陶耍,我們只看內(nèi)置的Interceptor,而且我們只需要弄明白每一個(gè)Interceptor的前置和后置工作即可她混。
-
RetryAndFollowUpInterceptor
@Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { val realChain = chain as RealInterceptorChain var request = chain.request val call = realChain.call var followUpCount = 0 var priorResponse: Response? = null var newExchangeFinder = true var recoveredFailures = listOf<IOException>() while (true) { call.enterNetworkInterceptorExchange(request, newExchangeFinder) var response: Response var closeActiveExchange = true try { if (call.isCanceled()) { throw IOException("Canceled") } try { response = realChain.proceed(request) newExchangeFinder = true } catch (e: RouteException) { // The attempt to connect via a route failed. The request will not have been sent. if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) { throw e.firstConnectException.withSuppressed(recoveredFailures) } else { recoveredFailures += e.firstConnectException } newExchangeFinder = false continue } catch (e: IOException) { // An attempt to communicate with a server failed. The request may have been sent. if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) { throw e.withSuppressed(recoveredFailures) } else { recoveredFailures += e } newExchangeFinder = false continue } // 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() } val exchange = call.interceptorScopedExchange val followUp = followUpRequest(response, exchange) if (followUp == null) { if (exchange != null && exchange.isDuplex) { call.timeoutEarlyExit() } closeActiveExchange = false return response } val followUpBody = followUp.body if (followUpBody != null && followUpBody.isOneShot()) { closeActiveExchange = false return response } response.body?.closeQuietly() if (++followUpCount > MAX_FOLLOW_UPS) { throw ProtocolException("Too many follow-up requests: $followUpCount") } request = followUp priorResponse = response } finally { call.exitNetworkInterceptorExchange(closeActiveExchange) } } }
點(diǎn)進(jìn)
RetryAndFollowUpInterceptor
看下它的intercept
函數(shù)烈钞,我k授药,代碼有點(diǎn)多随静,不要慌莺褒,穩(wěn)住闰非,按前,中僚楞,后置分開來看就好了岸裙。-
前置工作建芙。
call.enterNetworkInterceptorExchange(request, newExchangeFinder)
這里我們先放一放它的前置工作来累,我們先分析中置和后置工作砚作。因?yàn)閺乃拿只蛘叽a中的while(true)可以看出來,它是做連接失敗重連和重定向跟蹤的功能嘹锁,我們仔細(xì)想一下它的工作流程就能明白它的工作具體是干什么的葫录。
-
中置工作
realChain.proceed(request)
交棒給下一個(gè)Interceptor。
-
后置工作
try { response = realChain.proceed(request) newExchangeFinder = true } catch (e: RouteException) { // The attempt to connect via a route failed. The request will not have been sent. if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) { throw e.firstConnectException.withSuppressed(recoveredFailures) } else { recoveredFailures += e.firstConnectException } newExchangeFinder = false continue } catch (e: IOException) { // An attempt to communicate with a server failed. The request may have been sent. if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) { throw e.withSuppressed(recoveredFailures) } else { recoveredFailures += e } newExchangeFinder = false continue } // 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() } val exchange = call.interceptorScopedExchange val followUp = followUpRequest(response, exchange) if (followUp == null) { if (exchange != null && exchange.isDuplex) { call.timeoutEarlyExit() } closeActiveExchange = false return response } val followUpBody = followUp.body if (followUpBody != null && followUpBody.isOneShot()) { closeActiveExchange = false return response } response.body?.closeQuietly() if (++followUpCount > MAX_FOLLOW_UPS) { throw ProtocolException("Too many follow-up requests: $followUpCount") } request = followUp priorResponse = response } finally { call.exitNetworkInterceptorExchange(closeActiveExchange) }
代碼中除了
response = realChain.proceed(request);
這句代碼之外领猾,剩下的都是它的后置工作压昼。
它的后置工作分為兩類,第一個(gè)各種異常和ResponseCode->3XX瘤运,都需要continue
,再轉(zhuǎn)一次匠题;第二類請求成功拯坟,直接return response
,結(jié)束循環(huán)韭山。
接下來我們具體看下整個(gè)后置工作:
!recover(e.lastConnectException, call, request, requestSendStarted = false)
這句代碼的意思就是出現(xiàn)異常的情況下郁季,判斷當(dāng)前狀態(tài)是否可以繼續(xù)重試發(fā)起請求冷溃,我們點(diǎn)進(jìn)去看下具體邏輯:private fun recover( e: IOException, call: RealCall, userRequest: Request, requestSendStarted: Boolean ): Boolean { // The application layer has forbidden retries. // 用戶是否設(shè)置了連接失敗重連,默認(rèn)為true梦裂,我們在之前的OkHttpClient中已經(jīng)分析了這個(gè)配置了似枕。 if (!client.retryOnConnectionFailure) return false // We can't send the request body again. //以下情況是不可以重連的,已經(jīng)開始發(fā)送請求并且該請求只能發(fā)送一次 if (requestSendStarted && requestIsOneShot(e, userRequest)) return false // This exception is fatal. // 根據(jù)異常類型判斷是否可以恢復(fù)重連 if (!isRecoverable(e, requestSendStarted)) return false // No more routes to attempt. // 當(dāng)連接失敗時(shí)判斷是否還有其他線路可以重連 if (!call.retryAfterFailure()) return false // For failure recovery, use the same route selector with a new connection. // 走到這里說明該請求確實(shí)可以進(jìn)行重連年柠。 return true }
注釋其實(shí)已經(jīng)說的很明白凿歼,這段代碼沒什么難度,以上代碼是出錯(cuò)情況下來判斷是否可以重連冗恨。
下面代碼則是正常響應(yīng)的情況下來判斷是否需要重連答憔,主要是處理重定向。// 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() } val exchange = call.interceptorScopedExchange val followUp = followUpRequest(response, exchange) if (followUp == null) { if (exchange != null && exchange.isDuplex) { call.timeoutEarlyExit() } closeActiveExchange = false return response } val followUpBody = followUp.body if (followUpBody != null && followUpBody.isOneShot()) { closeActiveExchange = false return response } response.body?.closeQuietly() if (++followUpCount > MAX_FOLLOW_UPS) { throw ProtocolException("Too many follow-up requests: $followUpCount") } request = followUp priorResponse = response
接下里重點(diǎn)看下
val followUp = followUpRequest(response, exchange)
掀抹;
這個(gè)方法的主要作用就是根據(jù)響應(yīng)碼來判斷是否需要重連虐拓,我們點(diǎn)進(jìn)去看下:@Throws(IOException::class) private fun followUpRequest(userResponse: Response, exchange: Exchange?):Request? { val route = exchange?.connection?.route() val responseCode = userResponse.code val method = userResponse.request.method when (responseCode) { HTTP_PROXY_AUTH -> { val selectedProxy = route!!.proxy if (selectedProxy.type() != Proxy.Type.HTTP) { throw ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy") } return client.proxyAuthenticator.authenticate(route, userResponse) } HTTP_UNAUTHORIZED -> return client.authenticator.authenticate(route, userResponse) HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> { return buildRedirectRequest(userResponse, method) } HTTP_CLIENT_TIMEOUT -> { // 408's are rare in practice, but some servers like HAProxy use this response code. The // spec says that we may repeat the request without modifications. Modern browsers also // repeat the request (even non-idempotent ones.) if (!client.retryOnConnectionFailure) { // The application layer has directed us not to retry the request. return null } val requestBody = userResponse.request.body if (requestBody != null && requestBody.isOneShot()) { return null } val priorResponse = userResponse.priorResponse if (priorResponse != null && priorResponse.code == HTTP_CLIENT_TIMEOUT) { // We attempted to retry and got another timeout. Give up. return null } if (retryAfter(userResponse, 0) > 0) { return null } return userResponse.request } HTTP_UNAVAILABLE -> { val priorResponse = userResponse.priorResponse if (priorResponse != null && priorResponse.code == HTTP_UNAVAILABLE) { // We attempted to retry and got another timeout. Give up. return null } if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) { // specifically received an instruction to retry without delay return userResponse.request } return null } HTTP_MISDIRECTED_REQUEST -> { // OkHttp can coalesce HTTP/2 connections even if the domain names are different. See // RealConnection.isEligible(). If we attempted this and the server returned HTTP 421, then // we can retry on a different connection. val requestBody = userResponse.request.body if (requestBody != null && requestBody.isOneShot()) { return null } if (exchange == null || !exchange.isCoalescedConnection) { return null } exchange.connection.noCoalescedConnections() return userResponse.request } else -> return null } }
代碼雖然不少,但是卻很好理解傲武。主要是根據(jù)
ResponseCode
處理重定向的問題蓉驹,就是3XX系列。HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> { return buildRedirectRequest(userResponse, method) }
這句代碼如果是3XX系列揪利,根據(jù)重定向的地址來重新構(gòu)建Request态兴,然后復(fù)制給
request
,在下一輪重新發(fā)起請求土童。
除了以上兩種情況是需要重連的诗茎,之外的就是直接返回Response
。
RetryAndFollowUpInterceptor
的中置和后置我們看完了献汗,之后我們來仔細(xì)看下它的前置工作是干什么的敢订,我們點(diǎn)進(jìn)去:
這是RealCall
類中的一個(gè)函數(shù),也就是說所有的鏈?zhǔn)焦ぷ髌鋵?shí)都是在為RealCall
來服務(wù)的罢吃,RealCall
才是爸爸楚午,記住這點(diǎn),后面的代碼就很好理解了尿招。fun enterNetworkInterceptorExchange(request: Request, newExchangeFinder: Boolean) { check(interceptorScopedExchange == null) synchronized(this) { check(!responseBodyOpen) { "cannot make a new request because the previous response is still open: " + "please call response.close()" } check(!requestBodyOpen) } if (newExchangeFinder) { this.exchangeFinder = ExchangeFinder( connectionPool, createAddress(request.url), this, eventListener ) } }
前面
synchronized
中的代碼都是對Request/Response
body做檢測的矾柜,目的就是保證發(fā)起新的請求前,上一個(gè)body要關(guān)閉掉就谜,避免數(shù)據(jù)混亂怪蔑。
后面的代碼才是關(guān)鍵。if (newExchangeFinder) { this.exchangeFinder = ExchangeFinder( connectionPool, createAddress(request.url), this, eventListener ) }
可以這樣理解丧荐,每一次請求缆瓣,都意味著需要一次數(shù)據(jù)交換,這個(gè)方法就是尋找一個(gè)數(shù)據(jù)交換虹统?弓坞?隧甚?啥意思?渡冻?戚扳?換個(gè)名詞來說,可能就會(huì)明白了族吻,尋找一個(gè)交換帽借,其實(shí)就是在尋找一個(gè)連接
Connection
,這樣是不是就明白了呼奢,就是為了尋找一個(gè)可用的TCP或者TLS的連接宜雀。但是呢, 其實(shí)這里并不是尋找連接,而是為尋找連接來做準(zhǔn)備的握础,就是準(zhǔn)備各種參數(shù)辐董,至于尋找連接,那是在其他Interceptor中來進(jìn)行的工作禀综。
這樣我們就大致明白RetryAndFollowUpInterceptor
的工作內(nèi)容了:- 連接準(zhǔn)備
- 請求
- 處理重連
-
到這里RetryAndFollowUpInterceptor
代碼就看完了简烘。
接下來看下一個(gè)Interceptor,BridgeInterceptor
定枷。
BridgeInterceptor
橋接Interceptor孤澎??欠窒?干啥用的覆旭?我們點(diǎn)進(jìn)去看下就明白了。
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val userRequest = chain.request()
val requestBuilder = userRequest.newBuilder()
val body = userRequest.body
if (body != null) {
val contentType = body.contentType()
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString())
}
val contentLength = body.contentLength()
if (contentLength != -1L) {
requestBuilder.header("Content-Length", contentLength.toString())
requestBuilder.removeHeader("Transfer-Encoding")
} else {
requestBuilder.header("Transfer-Encoding", "chunked")
requestBuilder.removeHeader("Content-Length")
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", userRequest.url.toHostHeader())
}
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.
var transparentGzip = false
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true
requestBuilder.header("Accept-Encoding", "gzip")
}
val cookies = cookieJar.loadForRequest(userRequest.url)
if (cookies.isNotEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies))
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", userAgent)
}
val networkResponse = chain.proceed(requestBuilder.build())
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
val responseBuilder = networkResponse.newBuilder()
.request(userRequest)
if (transparentGzip &&
"gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
networkResponse.promisesBody()) {
val responseBody = networkResponse.body
if (responseBody != null) {
val gzipSource = GzipSource(responseBody.source())
val strippedHeaders = networkResponse.headers.newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build()
responseBuilder.headers(strippedHeaders)
val contentType = networkResponse.header("Content-Type")
responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
}
}
return responseBuilder.build()
}
代碼很多岖妄,但是我們大致一看就知道是干什么的了型将。大致的工作就是為上一步準(zhǔn)備的請求和下一步即將發(fā)送的請求做一個(gè)連接。
其實(shí)說白了就是為我們添加各種必要但對于開發(fā)者來說比較麻煩的各種Header
信息荐虐,所以一些必要且麻煩的Header
OkHttp幫我們做了七兜。比如:Content-Length,Content-Encoding等各種Header福扬。還是老規(guī)矩腕铸,來分割下前、中铛碑、后置工作狠裹。
- 前置工作
添加各種Header。 - 中置工作
發(fā)起請求汽烦。 - 后置工作
因?yàn)榍爸霉ぷ鲙臀覀兲砑恿艘恍ゞzip Header酪耳,所以后置工作需要幫我們進(jìn)行解壓縮。
這個(gè)BridgeInterceptor
相對來收是比較簡單的,接下來我們看下一個(gè)Interceptor碗暗。
CacheInterceptor
,顧名思義就是做緩存工作用的Interceptor梢夯。請求之前先看下是否有可用緩存言疗,如果有,就直接用緩存颂砸,如果沒有則發(fā)起請求噪奄,請求結(jié)束后判斷是否可以緩存,如果可以則進(jìn)行緩存人乓,這就是它大概的一個(gè)工作內(nèi)容勤篮,我們仔細(xì)想一下就可以想明白的。
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val call = chain.call()
val cacheCandidate = cache?.get(chain.request())
val now = System.currentTimeMillis()
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
val networkRequest = strategy.networkRequest
val cacheResponse = strategy.cacheResponse
cache?.trackResponse(strategy)
val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE
if (cacheCandidate != null && cacheResponse == null) {
// The cache candidate wasn't applicable. Close it.
cacheCandidate.body?.closeQuietly()
}
// If we're forbidden from using the network and the cache is insufficient, fail.
if (networkRequest == null && cacheResponse == null) {
return Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(HTTP_GATEWAY_TIMEOUT)
.message("Unsatisfiable Request (only-if-cached)")
.body(EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build().also {
listener.satisfactionFailure(call, it)
}
}
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build().also {
listener.cacheHit(call, it)
}
}
if (cacheResponse != null) {
listener.cacheConditionalHit(call, cacheResponse)
} else if (cache != null) {
listener.cacheMiss(call)
}
var networkResponse: Response? = 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) {
cacheCandidate.body?.closeQuietly()
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (networkResponse?.code == HTTP_NOT_MODIFIED) {
val 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.also {
listener.cacheHit(call, it)
}
} else {
cacheResponse.body?.closeQuietly()
}
}
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
if (cache != null) {
if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response).also {
if (cacheResponse != null) {
// This will log a conditional cache miss only.
listener.cacheMiss(call)
}
}
}
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
// The cache cannot be written.
}
}
}
return response
}
額色罚。碰缔。。挺多代碼的戳护。還是老規(guī)矩金抡,分割前、中腌且、后置工作梗肝。
- 前置工作
檢查是否有可用緩存,如果有則直接返回緩存內(nèi)容铺董,代碼這里就不分析了巫击,比較簡單。val call = chain.call() val cacheCandidate = cache?.get(chain.request()) val now = System.currentTimeMillis() val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute() val networkRequest = strategy.networkRequest val cacheResponse = strategy.cacheResponse cache?.trackResponse(strategy) val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE if (cacheCandidate != null && cacheResponse == null) { // The cache candidate wasn't applicable. Close it. cacheCandidate.body?.closeQuietly() } // If we're forbidden from using the network and the cache is insufficient, fail. if (networkRequest == null && cacheResponse == null) { return Response.Builder() .request(chain.request()) .protocol(Protocol.HTTP_1_1) .code(HTTP_GATEWAY_TIMEOUT) .message("Unsatisfiable Request (only-if-cached)") .body(EMPTY_RESPONSE) .sentRequestAtMillis(-1L) .receivedResponseAtMillis(System.currentTimeMillis()) .build().also { listener.satisfactionFailure(call, it) } } // If we don't need the network, we're done. if (networkRequest == null) { return cacheResponse!!.newBuilder() .cacheResponse(stripBody(cacheResponse)) .build().also { listener.cacheHit(call, it) } } if (cacheResponse != null) { listener.cacheConditionalHit(call, cacheResponse) } else if (cache != null) { listener.cacheMiss(call) }
- 中置工作
發(fā)起請求精续。networkResponse = chain.proceed(networkRequest)
- 后置工作
根據(jù)Response來判斷是否需要緩存坝锰,同樣代碼也是很簡單,這里就不細(xì)看了驻右。if (cacheResponse != null) { if (networkResponse?.code == HTTP_NOT_MODIFIED) { val 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.also { listener.cacheHit(call, it) } } else { cacheResponse.body?.closeQuietly() } } val response = networkResponse!!.newBuilder() .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build() if (cache != null) { if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) { // Offer this request to the cache. val cacheRequest = cache.put(response) return cacheWritingResponse(cacheRequest, response).also { if (cacheResponse != null) { // This will log a conditional cache miss only. listener.cacheMiss(call) } } } if (HttpMethod.invalidatesCache(networkRequest.method)) { try { cache.remove(networkRequest) } catch (_: IOException) { // The cache cannot be written. } } }
但是這里我們可能需要簡單了解下CacheStrategy什黑,緩存策略,其實(shí)就是根據(jù)不同的緩存Header來進(jìn)行不同的緩存策略堪夭。比如:Date愕把,Expires,Last-Modified森爽,ETag恨豁,Age。然后再結(jié)合ReponseHeader來執(zhí)行具體的緩存策略爬迟。比如:If-None-Match橘蜜,If-Modified-Since 。這里就不細(xì)看了,都比較簡單计福,接下來進(jìn)入下一個(gè)Interceptor跌捆。
ConnectInterceptor
,連接Interceptor象颖,這個(gè)是重點(diǎn)佩厚,這個(gè)是重點(diǎn),這個(gè)是重點(diǎn)说订。
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.call.initExchange(chain)
val connectedChain = realChain.copy(exchange = exchange)
return connectedChain.proceed(realChain.request)
}
哇~代碼很少啊抄瓦,只有四行代碼。啪啪啪 ~~~ 別看代碼少陶冷,但是卻很難懂钙姊,硬核的東西比較多。
老規(guī)矩埂伦,分割前煞额、中、后置工作赤屋。
- 前置工作
val realChain = chain as RealInterceptorChain val exchange = realChain.call.initExchange(chain) val connectedChain = realChain.copy(exchange = exchange)
- 中置工作
connectedChain.proceed(realChain.request)
- 后置工作立镶,無,卒~类早。
啊媚媒,為什么沒有后置工作,看名字涩僻,ConnectInterceptor缭召,它是建立連接的Interceptor,連接建立逆日,請求嵌巷,返回結(jié)果,還要什么后置工作室抽。
接下來我們具體分析代碼搪哪。
我們先來看下前置工作;
val exchange = realChain.call.initExchange(chain)
然后我們點(diǎn)進(jìn)去看下代碼:
internal fun initExchange(chain: RealInterceptorChain): Exchange {
synchronized(this) {
check(expectMoreExchanges) { "released" }
check(!responseBodyOpen)
check(!requestBodyOpen)
}
val exchangeFinder = this.exchangeFinder!!
val codec = exchangeFinder.find(client, chain)
val result = Exchange(this, eventListener, exchangeFinder, codec)
this.interceptorScopedExchange = result
this.exchange = result
synchronized(this) {
this.requestBodyOpen = true
this.responseBodyOpen = true
}
if (canceled) throw IOException("Canceled")
return result
}
呦呵~又是RealCall
類中的一個(gè)函數(shù)坪圾,RealCall
果然是爸爸晓折。
我們看下重點(diǎn)的代碼:
val codec = exchangeFinder.find(client, chain)
val result = Exchange(this, eventListener, exchangeFinder, codec)
第一句代碼:通過find找到一個(gè)codec,這個(gè)codec是干嘛用的兽泄?其實(shí)是coder&decoder縮寫漓概,字面意思,編碼解碼器病梢。哦原來是查找一個(gè)可用的編碼解碼器胃珍,就是找到一個(gè)針對不同的格式報(bào)文的編碼解碼器,比如:HTTP1版本的編碼解碼器,HTTP2的編碼解碼器觅彰。我們點(diǎn)擊去看下:
fun find(
client: OkHttpClient,
chain: RealInterceptorChain
): ExchangeCodec {
try {
val resultConnection = findHealthyConnection(
connectTimeout = chain.connectTimeoutMillis,
readTimeout = chain.readTimeoutMillis,
writeTimeout = chain.writeTimeoutMillis,
pingIntervalMillis = client.pingIntervalMillis,
connectionRetryEnabled = client.retryOnConnectionFailure,
doExtensiveHealthChecks = chain.request.method != "GET"
)
return resultConnection.newCodec(client, chain)
} catch (e: RouteException) {
trackFailure(e.lastConnectException)
throw e
} catch (e: IOException) {
trackFailure(e)
throw RouteException(e)
}
}
其實(shí)只有兩行代碼吩蔑,第一行找到一個(gè)健康的連接,根據(jù)這個(gè)連接來創(chuàng)建對應(yīng)的codec對象填抬。什么是一個(gè)健康的連接哥纫?我們接著在點(diǎn)進(jìn)去看下:
@Throws(IOException::class)
private fun findHealthyConnection(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean,
doExtensiveHealthChecks: Boolean
): RealConnection {
while (true) {
val candidate = findConnection(
connectTimeout = connectTimeout,
readTimeout = readTimeout,
writeTimeout = writeTimeout,
pingIntervalMillis = pingIntervalMillis,
connectionRetryEnabled = connectionRetryEnabled
)
// Confirm that the connection is good.
if (candidate.isHealthy(doExtensiveHealthChecks)) {
return candidate
}
// If it isn't, take it out of the pool.
candidate.noNewExchanges()
// Make sure we have some routes left to try. One example where we may exhaust all the routes
// would happen if we made a new connection and it immediately is detected as unhealthy.
if (nextRouteToTry != null) continue
val routesLeft = routeSelection?.hasNext() ?: true
if (routesLeft) continue
val routesSelectionLeft = routeSelector?.hasNext() ?: true
if (routesSelectionLeft) continue
throw IOException("exhausted all routes")
}
}
兩部分代碼:第一部分找到一個(gè)連接,第二部分判斷連接是否是健康的痴奏。
我們從第一部分代碼點(diǎn)進(jìn)去看下是怎么樣找到一個(gè)連接的。
@Throws(IOException::class)
private fun findConnection(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean
): RealConnection {
if (call.isCanceled()) throw IOException("Canceled")
// Attempt to reuse the connection from the call.
val callConnection = call.connection // This may be mutated by releaseConnectionNoEvents()!
if (callConnection != null) {
var toClose: Socket? = null
synchronized(callConnection) {
if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
toClose = call.releaseConnectionNoEvents()
}
}
// If the call's connection wasn't released, reuse it. We don't call connectionAcquired() here
// because we already acquired it.
if (call.connection != null) {
check(toClose == null)
return callConnection
}
// The call's connection was released.
toClose?.closeQuietly()
eventListener.connectionReleased(call, callConnection)
}
// We need a new connection. Give it fresh stats.
refusedStreamCount = 0
connectionShutdownCount = 0
otherFailureCount = 0
// Attempt to get a connection from the pool.
if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
// Nothing in the pool. Figure out what route we'll try next.
val routes: List<Route>?
val route: Route
if (nextRouteToTry != null) {
// Use a route from a preceding coalesced connection.
routes = null
route = nextRouteToTry!!
nextRouteToTry = null
} else if (routeSelection != null && routeSelection!!.hasNext()) {
// Use a route from an existing route selection.
routes = null
route = routeSelection!!.next()
} else {
// Compute a new route selection. This is a blocking operation!
var localRouteSelector = routeSelector
if (localRouteSelector == null) {
localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
this.routeSelector = localRouteSelector
}
val localRouteSelection = localRouteSelector.next()
routeSelection = localRouteSelection
routes = localRouteSelection.routes
if (call.isCanceled()) throw IOException("Canceled")
// Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. We have a better chance of matching thanks to connection coalescing.
if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
route = localRouteSelection.next()
}
// Connect. Tell the call about the connecting call so async cancels work.
val newConnection = RealConnection(connectionPool, route)
call.connectionToCancel = newConnection
try {
newConnection.connect(
connectTimeout,
readTimeout,
writeTimeout,
pingIntervalMillis,
connectionRetryEnabled,
call,
eventListener
)
} finally {
call.connectionToCancel = null
}
call.client.routeDatabase.connected(newConnection.route())
// If we raced another call connecting to this host, coalesce the connections. This makes for 3
// different lookups in the connection pool!
if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) {
val result = call.connection!!
nextRouteToTry = route
newConnection.socket().closeQuietly()
eventListener.connectionAcquired(call, result)
return result
}
synchronized(newConnection) {
connectionPool.put(newConnection)
call.acquireConnectionNoEvents(newConnection)
}
eventListener.connectionAcquired(call, newConnection)
return newConnection
}
我k厌秒,這代碼著實(shí)有點(diǎn)長啊读拆。不慌,根據(jù)功能來劃分下鸵闪,慢慢分析檐晕。
第一部分:
val callConnection = call.connection // This may be mutated by releaseConnectionNoEvents()!
if (callConnection != null) {
var toClose: Socket? = null
synchronized(callConnection) {
if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
toClose = call.releaseConnectionNoEvents()
}
}
// If the call's connection wasn't released, reuse it. We don't call connectionAcquired() here
// because we already acquired it.
if (call.connection != null) {
check(toClose == null)
return callConnection
}
// The call's connection was released.
toClose?.closeQuietly()
eventListener.connectionReleased(call, callConnection)
}
先看下自己有沒有連接,如果有且是可重用的蚌讼,就直接返回辟灰。
第二部分:
if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
從連接池中獲取一個(gè)可用的連接,此時(shí)傳入的參數(shù):(address, call, null, false)
說明下最后兩個(gè)參數(shù)分別是:路由和是否多路復(fù)用(HTTP2的特性)
這里說個(gè)題外話篡石,說明是路由芥喇?
一個(gè)路由包含3部分:proxy(代理類型:代理或者直連),ip以及port凰萨,也就是說一個(gè)路由如果確定之后继控,有可能發(fā)生變化的是proxy和ip。用一張圖來說明下胖眷,如下:
我們點(diǎn)進(jìn)入看下這個(gè)allAcquirePooledConnection
這個(gè)函數(shù):
fun callAcquirePooledConnection(
address: Address,
call: RealCall,
routes: List<Route>?,
requireMultiplexed: Boolean
): Boolean {
for (connection in connections) {
synchronized(connection) {
if (requireMultiplexed && !connection.isMultiplexed) return@synchronized
if (!connection.isEligible(address, routes)) return@synchronized
call.acquireConnectionNoEvents(connection)
return true
}
}
return false
}
ok武通,代碼不多,主要是遍歷連接池中的連接珊搀,然后篩選連接冶忱,條件如下:
- 是否是需要多路復(fù)用的連接并且該鏈接是支持多路復(fù)用;
- 再看該該鏈接是否滿足要求:
這段代碼主要是闡述步驟二滿足的標(biāo)準(zhǔn)是什么境析。internal fun isEligible(address: Address, routes: List<Route>?): Boolean { assertThreadHoldsLock() // If this connection is not accepting new exchanges, we're done. if (calls.size >= allocationLimit || noNewExchanges) return false // If the non-host fields of the address don't overlap, we're done. if (!this.route.address.equalsNonHost(address)) return false // If the host exactly matches, we're done: this connection can carry the address. if (address.url.host == this.route().address.url.host) { return true // This connection is a perfect match. } // At this point we don't have a hostname match. But we still be able to carry the request if // our connection coalescing requirements are met. See also: // https://hpbn.co/optimizing-application-delivery/#eliminate-domain-sharding // https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing/ // 1. This connection must be HTTP/2. if (http2Connection == null) return false // 2. The routes must share an IP address. if (routes == null || !routeMatchesAny(routes)) return false // 3. This connection's server certificate's must cover the new host. if (address.hostnameVerifier !== OkHostnameVerifier) return false if (!supportsUrl(address.url)) return false // 4. Certificate pinning must match the host. try { address.certificatePinner!!.check(address.url.host, handshake()!!.peerCertificates) } catch (_: SSLPeerUnverifiedException) { return false } return true // The caller's address can be carried by this connection. }
當(dāng)前連接的數(shù)量小于最大限制且可以繼續(xù)創(chuàng)建新的
Exechange
-
當(dāng)前連接的地址和新連接的地址不相同囚枪,需滿足以下條件:
-
internal fun equalsNonHost(that: Address): Boolean { return this.dns == that.dns && this.proxyAuthenticator == that.proxyAuthenticator && this.protocols == that.protocols && this.connectionSpecs == that.connectionSpecs && this.proxySelector == that.proxySelector && this.proxy == that.proxy && this.sslSocketFactory == that.sslSocketFactory && this.hostnameVerifier == that.hostnameVerifier && this.certificatePinner == that.certificatePinner && this.url.port == that.url.port }
這里兩個(gè)地址是否相同,代碼已經(jīng)寫的很清楚了簿晓,這里就不在贅述眶拉。
-
主機(jī)名是否相同
是否為http2的連接
-
判斷路由是否匹配
private fun routeMatchesAny(candidates: List<Route>): Boolean { return candidates.any { it.proxy.type() == Proxy.Type.DIRECT && route.proxy.type() == Proxy.Type.DIRECT && route.socketAddress == it.socketAddress } }
驗(yàn)證碼hostnameVerifier是否一致,這里驗(yàn)證的過程我們就不一一具體分析了憔儿。
檢測certificatePinner忆植。
如果以上條件都沒問題,也就是弄明白了該鏈接滿足了當(dāng)前的需求,繼續(xù)下一步:
取得連接且不發(fā)送事件朝刊。call.acquireConnectionNoEvents(connection)
第三步:指定路由耀里,然后在拿一次不支持http2的了解;
if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
這個(gè)時(shí)候傳入的參數(shù)為:(address, call, routes, false)
拾氓,可以看到這個(gè)時(shí)候已經(jīng)傳入了路由參數(shù)冯挎。這時(shí)候得到的連接就是從這一組路由中得到不支持http2的連接。
如果沒有得到連接咙鞍,進(jìn)入第四步房官;
第四步:創(chuàng)建一個(gè)新的連接
val newConnection = RealConnection(connectionPool, route)
call.connectionToCancel = newConnection
try {
newConnection.connect(
connectTimeout,
readTimeout,
writeTimeout,
pingIntervalMillis,
connectionRetryEnabled,
call,
eventListener
)
} finally {
call.connectionToCancel = null
}
call.client.routeDatabase.connected(newConnection.route())
第五步:創(chuàng)建新的連接之后,再次沖連接池中再取一次续滋,而這次的入?yún)⒂肿兞耍?code>(address, call, routes, true)翰守,意思是只取路由中的支持多路服用的連接,如果取到了說明這組路由且支持多路復(fù)用的連接是存在連接池中的疲酌,然后從連接池中取出蜡峰,之后將剛剛新創(chuàng)建的連接關(guān)閉掉。
這里說明下為什么會(huì)出現(xiàn)這種情況呢朗恳?因?yàn)橥粫r(shí)間有可能同時(shí)發(fā)起多個(gè)請求湿颅,但是每個(gè)請求的host和schema是一樣的,只有path是不一樣的粥诫,這樣的請求是可以公用一個(gè)連接的油航,所以當(dāng)其中一個(gè)請求創(chuàng)建成功后,其他請求一定會(huì)拿到連接池中的連接臀脏,因?yàn)榈谝粋€(gè)創(chuàng)建成功后劝堪,會(huì)放入到連接池中,這個(gè)是同步的操作揉稚,當(dāng)放入到連接池之后秒啦,其他請求再去拿一定是有連接可用的。
這里額外說一下:
nextRouteToTry
搀玖,這個(gè)是干嘛用的呢余境?就是第四步和第五步的結(jié)合,第四步創(chuàng)建一個(gè)新的連接灌诅,既然能創(chuàng)建一個(gè)新的連接芳来,說明這個(gè)路由是可用的,當(dāng)出現(xiàn)錯(cuò)誤需要重連時(shí)猜拾,此時(shí)就不需要在重新查找路由了即舌,因?yàn)檫@個(gè)創(chuàng)建的連接對應(yīng)的路由是一定可用的,目的是為了加快連接速度挎袜。
經(jīng)過上述五步之后顽聂,一定會(huì)拿到連接肥惭,所以一共經(jīng)歷了五種獲取連接的情況。
然后findConnection
函數(shù)就結(jié)束了紊搪,返回了一個(gè)連接蜜葱。然后判斷該鏈接是否健康,健康與否主要是有是否關(guān)閉來判斷的耀石。然后接著看返回之后的代碼牵囤。
val resultConnection = findHealthyConnection(
connectTimeout = chain.connectTimeoutMillis,
readTimeout = chain.readTimeoutMillis,
writeTimeout = chain.writeTimeoutMillis,
pingIntervalMillis = client.pingIntervalMillis,
connectionRetryEnabled = client.retryOnConnectionFailure,
doExtensiveHealthChecks = chain.request.method != "GET"
)
return resultConnection.newCodec(client, chain)
在得到一個(gè)健康的連接之后,然后使用這個(gè)連接創(chuàng)建出一個(gè)編碼解碼器滞伟。
我們點(diǎn)進(jìn)去看下是怎樣創(chuàng)建編碼解碼器的:
@Throws(SocketException::class)
internal fun newCodec(client: OkHttpClient, chain: RealInterceptorChain): ExchangeCodec {
val socket = this.socket!!
val source = this.source!!
val sink = this.sink!!
val http2Connection = this.http2Connection
return if (http2Connection != null) {
Http2ExchangeCodec(client, this, chain, http2Connection)
} else {
socket.soTimeout = chain.readTimeoutMillis()
source.timeout().timeout(chain.readTimeoutMillis.toLong(), MILLISECONDS)
sink.timeout().timeout(chain.writeTimeoutMillis.toLong(), MILLISECONDS)
Http1ExchangeCodec(client, this, source, sink)
}
}
哦原來是根據(jù)連接的不同來創(chuàng)建不同協(xié)議的編碼解碼器揭鳞,如果是http1,就創(chuàng)建http1的編碼解碼器梆奈,如果是http2汹桦,就創(chuàng)建http2的編碼解碼器。ok鉴裹。接著往下走。
接著回溯代碼:
internal fun initExchange(chain: RealInterceptorChain): Exchange {
synchronized(this) {
check(expectMoreExchanges) { "released" }
check(!responseBodyOpen)
check(!requestBodyOpen)
}
val exchangeFinder = this.exchangeFinder!!
val codec = exchangeFinder.find(client, chain)
val result = Exchange(this, eventListener, exchangeFinder, codec)
this.interceptorScopedExchange = result
this.exchange = result
synchronized(this) {
this.requestBodyOpen = true
this.responseBodyOpen = true
}
if (canceled) throw IOException("Canceled")
return result
}
可以看到在得到編碼解碼器之后钥弯,基于編碼解碼器來創(chuàng)建一個(gè)Exchange
径荔。
來解釋下Codec和Exchange:
Codec是真正的發(fā)起請求和接受請求的,而Exchange是對Codec的包裝和代理脆霎,exchange中的讀寫數(shù)據(jù)总处,其實(shí)就是調(diào)用了codec中的讀寫數(shù)據(jù)。
好了睛蛛,接著網(wǎng)上回溯代碼鹦马,就會(huì)到了最開始initExchange
函數(shù)入口的地方了:
object ConnectInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.call.initExchange(chain)
val connectedChain = realChain.copy(exchange = exchange)
return connectedChain.proceed(realChain.request)
}
}
然后ConnectInterceptor
進(jìn)行中置工作(交棒):
connectedChain.proceed(realChain.request)
ok,到這里整個(gè)ConnectInterceptor
工作就分析完了忆肾,就是創(chuàng)建連接荸频,然后返回一個(gè)包裝好的Exchange,有了這個(gè)Exchange就可以做到發(fā)送和接受請求了客冈。接下來我們看最后一個(gè)Interceptor
旭从, CallServerInterceptor,點(diǎn)擊去看下:
class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.exchange!!
val request = realChain.request
val requestBody = request.body
val sentRequestMillis = System.currentTimeMillis()
exchange.writeRequestHeaders(request)
var invokeStartEvent = true
var responseBuilder: Response.Builder? = null
if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
exchange.flushRequest()
responseBuilder = exchange.readResponseHeaders(expectContinue = true)
exchange.responseHeadersStart()
invokeStartEvent = false
}
if (responseBuilder == null) {
if (requestBody.isDuplex()) {
// Prepare a duplex body so that the application can send a request body later.
exchange.flushRequest()
val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
requestBody.writeTo(bufferedRequestBody)
} else {
// Write the request body if the "Expect: 100-continue" expectation was met.
val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
requestBody.writeTo(bufferedRequestBody)
bufferedRequestBody.close()
}
} else {
exchange.noRequestBody()
if (!exchange.connection.isMultiplexed) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
exchange.noNewExchangesOnConnection()
}
}
} else {
exchange.noRequestBody()
}
if (requestBody == null || !requestBody.isDuplex()) {
exchange.finishRequest()
}
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
if (invokeStartEvent) {
exchange.responseHeadersStart()
invokeStartEvent = false
}
}
var response = responseBuilder
.request(request)
.handshake(exchange.connection.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
var 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 status.
responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
if (invokeStartEvent) {
exchange.responseHeadersStart()
}
response = responseBuilder
.request(request)
.handshake(exchange.connection.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
code = response.code
}
exchange.responseHeadersEnd(response)
response = if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response.newBuilder()
.body(EMPTY_RESPONSE)
.build()
} else {
response.newBuilder()
.body(exchange.openResponseBody(response))
.build()
}
if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
"close".equals(response.header("Connection"), ignoreCase = true)) {
exchange.noNewExchangesOnConnection()
}
if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
throw ProtocolException(
"HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
}
return response
}
}
額场仲,又是一坨代碼和悦,不慌,點(diǎn)根煙安靜下渠缕,可以發(fā)現(xiàn)鸽素,這就是在利用Exchange來進(jìn)行寫請求和讀響應(yīng)啊,然后對響應(yīng)進(jìn)行處理亦鳞,將最終的響應(yīng)直接返回馍忽,這樣這個(gè)鏈?zhǔn)秸{(diào)用就開始往回走了棒坏。看下重點(diǎn)代碼:
exchange.writeRequestHeaders(request)
寫請求頭信息
exchange.flushRequest()
結(jié)束請求寫操作舵匾。
exchange.readResponseHeaders(expectContinue = false)
讀取響應(yīng)頭俊抵。
response.newBuilder()
.body(exchange.openResponseBody(response))
.build()
讀取響應(yīng)體,構(gòu)建成一個(gè)響應(yīng)體:Response坐梯,然后返回徽诲。
接下來的過程就很熟悉了:
- networkInterceptors,如果設(shè)置了自定義網(wǎng)絡(luò)攔截器吵血,會(huì)從這里進(jìn)行Response的回調(diào)谎替;
- ConnectInterceptor,沒有后置工作蹋辅,直接返回Response钱贯;
- CacheInterceptor,根據(jù)響應(yīng)頭來處理緩存侦另,之后返回Response秩命;
- BridgeInterceptor,將響應(yīng)體進(jìn)行g(shù)zip解壓縮褒傅,然后返回Response弃锐;
- RetryAndFollowUpInterceptor,根據(jù)響應(yīng)碼來判斷是否重連殿托,如不需要霹菊,返回Response
- interceptors,如果用戶設(shè)置了自定的Interceptors支竹,這里會(huì)進(jìn)行Response的回調(diào)旋廷。
之后這個(gè)鏈?zhǔn)焦ぷ鹘Y(jié)束,回到getResponseWithInterceptorChain
這個(gè)函數(shù)礼搁,還記得這個(gè)函數(shù)在哪調(diào)用的嗎饶碘?哈哈哈。馒吴。熊镣。對就是我們最開始分析鏈?zhǔn)焦ぷ鞯牡胤剑?br>
AsyncCall中的run
函數(shù)。
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
timeout.enter()
try {
val response = getResponseWithInterceptorChain()
signalledCallback = true
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
responseCallback.onFailure(this@RealCall, e)
}
} catch (t: Throwable) {
cancel()
if (!signalledCallback) {
val canceledException = IOException("canceled due to $t")
canceledException.addSuppressed(t)
responseCallback.onFailure(this@RealCall, canceledException)
}
throw t
} finally {
client.dispatcher.finished(this)
}
}
}
這之后就是responseCallback
的各種回調(diào)了募书。
至此這個(gè)異步請求就結(jié)束了绪囱,整體下來也不是太難理解,還是很開心的莹捡。
至于RealCall中的execute
函數(shù)(同步請求數(shù)據(jù))我們來簡單看下鬼吵;
override fun execute(): Response {
check(executed.compareAndSet(false, true)) { "Already Executed" }
timeout.enter()
callStart()
try {
client.dispatcher.executed(this)
return getResponseWithInterceptorChain()
} finally {
client.dispatcher.finished(this)
}
}
哎呦我去,簡單粗暴篮赢,直接調(diào)用getResponseWithInterceptorChain
函數(shù)齿椅,然后將結(jié)果直接返回琉挖。
好了,到這里整個(gè)OkHttp的源碼簡單解讀就結(jié)束涣脚,整體看來不是太難示辈,但也是需要多次進(jìn)行揣摩和分析的,相信對你來說小case啦遣蚀。加油~~~
個(gè)人能力有限矾麻,如有錯(cuò)誤之處,還望指出芭梯,我會(huì)第一時(shí)間驗(yàn)證并修改险耀。