不開心竹勉,就算長生不老也沒用,開心恐锦,就算只能活幾天也足夠!——《大話西游之月光寶盒》
前言
本文從源碼角度分析OkHttp的工作流程疆液,源碼基于4.4.0一铅。
OkHttp基本實現(xiàn)原理
OkHttp的內(nèi)部實現(xiàn)通過一個責(zé)任鏈模式完成,將網(wǎng)絡(luò)請求的各個階段封裝到各個鏈條中堕油,實現(xiàn)了各層的解耦潘飘。
OkHttp使用流程
第一步:創(chuàng)建OkHttpClient
OkHttpClient是okhttp3框架的客戶端,用于發(fā)送http請求(Requests)和讀取讀取網(wǎng)絡(luò)返回數(shù)據(jù)(Responses)掉缺。因為OkHttpClient擁有自己的連接池和線程池卜录,所以官方建議使用單例創(chuàng)建OkHttpClient,即一個進程中只創(chuàng)建一次即可眶明,以后的每次網(wǎng)絡(luò)請求都使用該實例艰毒。這樣做利于減少延遲和節(jié)省內(nèi)存。
創(chuàng)建OkHttpClient的兩種方式:
//方式一:使用默認網(wǎng)絡(luò)配置創(chuàng)建OkHttpClient實例
OkHttpClient client = new OkHttpClient();
//方式二:通過構(gòu)建者模式自定義網(wǎng)絡(luò)配置創(chuàng)建OkHttpClient實例
OkHttpClient client = new OkHttpClient.Builder()
...//通過Builder提供的方法設(shè)置網(wǎng)絡(luò)參數(shù)
.build();
OkHttpClient.Builder類的部分變量如下所示,通過其提供的方法可以設(shè)置部分參數(shù)的值
//調(diào)度器搜囱,實現(xiàn)同步 /異步的關(guān)鍵丑瞧,后文會進行講解
internal var dispatcher: Dispatcher = Dispatcher()
//連接池
internal var connectionPool: ConnectionPool = ConnectionPool()
//各種攔截器
internal val interceptors: MutableList<Interceptor> = mutableListOf()
internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
//連接失敗后自動重試,默認true
internal var retryOnConnectionFailure = true
internal var authenticator: Authenticator = Authenticator.NONE
internal var followRedirects = true
internal var followSslRedirects = true
//網(wǎng)絡(luò)請求Cookie
internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
//網(wǎng)絡(luò)請求的緩存
internal var cache: Cache? = null
internal var dns: Dns = Dns.SYSTEM
internal var proxy: Proxy? = null
internal var proxySelector: ProxySelector? = null
internal var proxyAuthenticator: Authenticator = Authenticator.NONE
internal var socketFactory: SocketFactory = SocketFactory.getDefault()
internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
internal var x509TrustManagerOrNull: X509TrustManager? = null
internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
internal var certificateChainCleaner: CertificateChainCleaner? = null
internal var callTimeout = 0
//網(wǎng)絡(luò)連接超時時間
internal var connectTimeout = 10_000
//數(shù)據(jù)讀取超時時間
internal var readTimeout = 10_000
//數(shù)據(jù)寫入超時時間
internal var writeTimeout = 10_000
internal var pingInterval = 0
internal var routeDatabase: RouteDatabase? = null
第二步:創(chuàng)建網(wǎng)絡(luò)請求描述對象Request
Request對象通過參數(shù)設(shè)置用于描述一次網(wǎng)絡(luò)請求犬辰。它的實例創(chuàng)建同樣采用了構(gòu)建者模式嗦篱,創(chuàng)建Request實例的代碼如下:
Request request = new Request.Builder().
url("換成要請求的url地址").
...//通過Builder提供的方法設(shè)置網(wǎng)絡(luò)參數(shù)
build();
Request.Builder()的部分源碼如下:
public open class Builder {
public constructor() { /* compiled code */ }
internal constructor(request: okhttp3.Request) { /* compiled code */ }
internal final var body: okhttp3.RequestBody? /* compiled code */
internal final var headers: okhttp3.Headers.Builder /* compiled code */
internal final var method: kotlin.String /* compiled code */
internal final var url: okhttp3.HttpUrl? /* compiled code */
...//省略了部分對外提供的參數(shù)設(shè)置方法
}
可見通過Request.Builder()可以設(shè)置如下常用的網(wǎng)絡(luò)參數(shù):
headers:請求頭;
body:請求體幌缝;
method:請求方式,比如:get/post/put...等請求方式诫欠;
url:請求地址涵卵;
第三步:創(chuàng)建網(wǎng)絡(luò)請求對象Call
Call為一個接口浴栽,其實現(xiàn)類為RealCall。一個Call對象封裝表示了一次網(wǎng)絡(luò)請求轿偎,通過它能夠執(zhí)行或者取消一次網(wǎng)絡(luò)請求典鸡,并且每一次網(wǎng)絡(luò)請求都會生產(chǎn)一個新的Call對象,同一個Call對象不能被執(zhí)行兩次坏晦。Call對象的創(chuàng)建需要用到前面創(chuàng)建好的OkHttpClient對象的newCall(Request request)方法萝玷,并需要將第二步創(chuàng)建好的用于描述此次網(wǎng)絡(luò)請求的Request對象作為參數(shù)傳進去,整體代碼如下:
OkHttpClient client = new OkHttpClient.Builder()
...//通過Builder提供的方法設(shè)置網(wǎng)絡(luò)參數(shù)
.build();
Request request = new Request.Builder().
url("換成要請求的url地址").
...//通過Builder提供的方法設(shè)置網(wǎng)絡(luò)參數(shù)
build();
Call call = client.newCall(request);
我們來看OkHttpClient的newCall(Request request)方法昆婿,其源碼如下:
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
可見最后創(chuàng)建的是RealCall對象球碉。
第四步:通過Call對象執(zhí)行網(wǎng)絡(luò)請求
okhttp3中提供了兩種網(wǎng)絡(luò)請求方式:一種是同步請求,調(diào)用call.execute()方法仓蛆;另一種是異步請求睁冬,調(diào)用call.enqueue(Callback callback)方法。在分析兩種請求方式之前看疙,先來看看OkHttp中一個重要成員Dispatcher(調(diào)度器)豆拨。
Dispatcher(調(diào)度器)
Dispatcher是OkHttp的任務(wù)調(diào)度的核心類,負責(zé)管理同步和異步的請求能庆,并管理每一個請求任務(wù)的請求狀態(tài)施禾,其內(nèi)部維護了一個線程池用于執(zhí)行相應(yīng)的請求。Dispatcher定義默認的最大并發(fā)請求量 maxRequests = 64 和單個host支持的最大并發(fā)量 maxRequestsPerHost = 5搁胆。并且在其內(nèi)部維護了三個雙端阻塞隊列用于管理網(wǎng)絡(luò)任務(wù)拾积,代碼如下:
class Dispatcher constructor() {
...
//默認的最大并發(fā)請求量為64
@get:Synchronized var maxRequests = 64
...
//默認單個host支持的最大并發(fā)量為5
@get:Synchronized var maxRequestsPerHost = 5
...
/** 存儲準備好的異步調(diào)用任務(wù),它們將順序執(zhí)行 */
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
/**存儲異步運行中的網(wǎng)絡(luò)任務(wù)丰涉,包括已經(jīng)被取消但還未結(jié)束的網(wǎng)絡(luò)任務(wù)*/
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
/**存儲同步運行中的網(wǎng)絡(luò)任務(wù)拓巧,包括已經(jīng)被取消但還未結(jié)束的網(wǎng)絡(luò)任務(wù)*/
private val runningSyncCalls = ArrayDeque<RealCall>()
...
}
關(guān)于這3個雙端阻塞隊列的作用,可以這么理解:把Dispatcher當成生產(chǎn)者一死,把線程池當成消費者肛度,當生產(chǎn)者生產(chǎn)的網(wǎng)絡(luò)請求數(shù)量大于消費者所能承受的最大范圍,就把未能及時執(zhí)行的任務(wù)保存在readyAsyncCalls隊列中投慈,當時機成熟承耿,也就是線程池有空余線程可以執(zhí)行時,會調(diào)用promoteCall()這個方法把等待隊列中的任務(wù)取出放到線程池中執(zhí)行伪煤,并且把這個任務(wù)轉(zhuǎn)移到runningAsyncCalls或者runningSyncCalls 隊列中去加袋。
為什么要使用雙端隊列?很簡單因為網(wǎng)絡(luò)請求執(zhí)行順序跟排隊一樣抱既,講究先來后到职烧,新來的請求放隊尾,執(zhí)行請求從對頭部取。我們知道LinkedList同樣也實現(xiàn)了Deque接口蚀之,內(nèi)部是用鏈表實現(xiàn)的雙端隊列蝗敢,那為什么不用LinkedList呢?實際上這與readyAsyncCalls向runningAsyncCalls轉(zhuǎn)換有關(guān),當執(zhí)行完一個請求或調(diào)用enqueue方法入隊新的請求時,會對readyAsyncCalls進行一次遍歷庙楚,將那些符合條件的等待請求轉(zhuǎn)移到runningAsyncCalls隊列中并交給線程池執(zhí)行。盡管二者都能完成這項任務(wù)讶泰,但是由于鏈表的數(shù)據(jù)結(jié)構(gòu)致使元素離散的分布在內(nèi)存的各個位置,CPU緩存無法帶來太多的便利拂到,另外在垃圾回收時痪署,使用數(shù)組結(jié)構(gòu)的效率要優(yōu)于鏈表。
接下來咱們結(jié)合上面說的Dispatcher分別看看同步請求和異步請求的實現(xiàn)過程谆焊,并詳細說一下他們是如何實現(xiàn)的惠桃。
請求方式一:同步請求
call.execute()源碼如下:
override fun execute(): Response {
synchronized(this) {
check(!executed) { "Already Executed" }
executed = true
}
timeout.enter()
callStart()
try {
//①調(diào)用調(diào)度器dispatcher的executed(Call call)方法
client.dispatcher.executed(this)
return getResponseWithInterceptorChain()
} finally {
//②調(diào)用分配器Dispatcher的finish()方法
client.dispatcher.finished(this)
}
}
注釋①部分調(diào)用了 client.dispatcher的executed()方法,再來看client.dispatcher的executed(call: RealCall)方法:
@Synchronized internal fun executed(call: RealCall) {
runningSyncCalls.add(call)
}
由此可見辖试,注釋①部分是將這次網(wǎng)絡(luò)請求RealCall實例存進了runningSyncCalls這雙端阻塞隊列辜王;
接著方法又調(diào)用了getResponseWithInterceptorChain()方法并返回。關(guān)于getResponseWithInterceptorChain()先放一邊罐孝,在異步請求中再一起講解呐馆。
接下來看注釋②,不管結(jié)果交易結(jié)果如何莲兢,都會調(diào)用finally中的client.dispatcher.finished(this)將本次請求從隊列中移除汹来。
請求方式二:異步請求
call.enqueue(Callback callback)源碼如下:
override fun enqueue(responseCallback: Callback) {
synchronized(this) {
check(!executed) { "Already Executed" }
executed = true
}
callStart()
//①調(diào)用調(diào)度器dispatcher的enqueue(AsyncCall call)方法
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
可見其內(nèi)部調(diào)用了client.dispatcher.enqueue(AsyncCall call)方法,分析該方法之前先來看看其參數(shù)AsyncCall類的定義:
internal inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {
@Volatile var callsPerHost = AtomicInteger(0)
private set
fun reuseCallsPerHostFrom(other: AsyncCall) {
this.callsPerHost = other.callsPerHost
}
val host: String
get() = originalRequest.url.host
val request: Request
get() = originalRequest
val call: RealCall
get() = this@RealCall
/**
* 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!
}
}
}
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)
}
}
}
}
跟蹤源碼發(fā)現(xiàn)AsyncCall是RealCall的一個內(nèi)部類改艇,其實現(xiàn)了Runnable接口收班,所以AsyncCall就是一個Runnable的實現(xiàn),當在線程池中執(zhí)行該對象時就會執(zhí)行其實現(xiàn)的run()方法谒兄,通過注釋①處源碼了解到和同步請求方法一樣它也會去調(diào)用getResponseWithInterceptorChain()方法摔桦。
現(xiàn)在回到client.dispatcher.enqueue(AsyncCall call)方法,其源碼如下:
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
// ①嘗試判斷并復(fù)用同一host請求的實例
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
可見方法會先將請求放入readyAsyncCalls雙端阻塞隊列承疲,然后會在注釋①出嘗試判斷并復(fù)用同一host請求的實例邻耕,findExistingCallWithHost(String host)源碼如下:
private fun findExistingCallWithHost(host: String): AsyncCall? {
for (existingCall in runningAsyncCalls) {
if (existingCall.host == host) return existingCall
}
for (existingCall in readyAsyncCalls) {
if (existingCall.host == host) return existingCall
}
return null
}
其作用為循環(huán)遍歷readyAsyncCalls和runningAsyncCalls列表尋找host值相同的call實例。如果找到Call實例就會調(diào)用call.reuseCallsPerHostFrom(AsyncCall other)方法燕鸽,其源碼如下:
internal inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {
@Volatile var callsPerHost = AtomicInteger(0)
private set
fun reuseCallsPerHostFrom(other: AsyncCall) {
this.callsPerHost = other.callsPerHost
}
}
其作用為給AsyncCall實例的成員變量callsPerHost賦值兄世,即共享同一host下AsyncCall請求實例的AtomicInteger值。
接著client.dispatcher.enqueue( AsyncCall call)最終會調(diào)用promoteAndExecute()方法啊研,其源碼如下:
private fun promoteAndExecute(): Boolean {
this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
val i = readyAsyncCalls.iterator()
//遍歷readyAsyncCalls
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.
//符合條件 從readyAsyncCalls列表中刪除
i.remove()
//per host 計數(shù)加1
asyncCall.callsPerHost.incrementAndGet()
executableCalls.add(asyncCall)
//移入runningAsyncCalls列表
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
//①提交任務(wù)到線程池
asyncCall.executeOn(executorService)
}
return isRunning
}
可見其主要邏輯為遍歷readyAsyncCalls隊列御滩,其中滿足閾值校驗的AsyncCall對象被移至runningAsyncCalls隊列同時提交到線程池執(zhí)行鸥拧。注釋①中可見提交到線程池使用到了executorService對象,現(xiàn)在來看看executorService的定義:
@get:Synchronized
@get:JvmName("executorService") val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
}
return executorServiceOrNull!!
}
這不是一個newCachedThreadPool嗎艾恼?沒錯住涉,除了最后一個threadFactory參數(shù)之外與newCachedThreadPool的源碼實現(xiàn)一模一樣麸锉,只不過是多設(shè)置了線程名字方便排查問題钠绍。其阻塞隊列采用的SynchronousQueue,它的特點是不存儲數(shù)據(jù)花沉,當添加一個元素時柳爽,必須等待一個消費線程取出它,否則一直阻塞碱屁,如果當前有空閑線程則直接在這個空閑線程執(zhí)行磷脯,如果沒有則新啟動一個線程執(zhí)行任務(wù)。通常用于需要快速響應(yīng)任務(wù)的場景娩脾,在網(wǎng)絡(luò)請求要求低延遲的大背景下比較合適赵誓。可見這個executorService對象就是Dispatcher自身維護的用于執(zhí)行網(wǎng)絡(luò)任務(wù)的線程池柿赊。
接著來看注釋①中執(zhí)行的AsyncCall.executeOn(ExecutorService executorService)方法的源碼:
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!
}
}
}
可見其功能為將AsyncCall對象加入到executorService線程池并執(zhí)行俩功,并在線程結(jié)束或者執(zhí)行失敗后清除AsyncCall請求對象。通過上面對AsyncCall類的分析可知碰声,AsyncCall類是一個Runnable的實現(xiàn)诡蜓,將asyncCall對象加入線程池執(zhí)行會去執(zhí)行其內(nèi)部實現(xiàn)的run()方法并最終調(diào)getResponseWithInterceptorChain()
方
法來獲取網(wǎng)絡(luò)請求的返回值。AsyncCall內(nèi)部實現(xiàn)的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)
}
}
}
在分析獲取網(wǎng)絡(luò)請求返回值getResponseWithInterceptorChain()方法之前再插一句胰挑,通過上面的分析蔓罚,不管同步請求還是異步請求,在請求正痴八蹋或者異常結(jié)束后都會調(diào)用了client.dispatcher.finished(call: RealCall)或client.dispatcher.finished(call: AsyncCall)方法豺谈,那我們就來看看其源碼做了什么:
//異步任務(wù)執(zhí)行結(jié)束
internal fun finished(call: AsyncCall) {
call.callsPerHost.decrementAndGet()
finished(runningAsyncCalls, call)
}
//同步任務(wù)執(zhí)行結(jié)束
internal fun finished(call: RealCall) {
finished(runningSyncCalls, call)
}
//同步異步任務(wù) 統(tǒng)一匯總到這里
private fun <T> finished(calls: Deque<T>, call: T) {
val idleCallback: Runnable?
synchronized(this) {
//將完成的任務(wù)從隊列中刪除
if (!calls.remove(call)) throw AssertionError("Call wasn't in-flight!")
idleCallback = this.idleCallback
}
//這個方法在前面已經(jīng)分析過了,用于將readyAsyncCalls隊列中的請求移入runningAsyncCalls隊列中贡这,并交由線程池執(zhí)行茬末。
val isRunning = promoteAndExecute()
//如果沒有請求需要執(zhí)行,回調(diào)閑置callback
if (!isRunning && idleCallback != null) {
idleCallback.run()
}
}
方法的主要邏輯已在注釋中寫出藕坯,需要注意的是promoteAndExecute()方法在enqueue和finish方法中都會調(diào)用团南,即當有新的請求入隊和當前請求完成后,需要重新提交一遍任務(wù)到線程池炼彪。
到目前為止我們通過源碼分析了解到吐根,其實OkHttp的同步請求call.execute()和異步請求call.enqueue(Callback callback)其實都是通過OkHttpClient中維護的Dispatcher調(diào)度器對象來實現(xiàn)的,而異步任務(wù)又是隊列的方式在Dispatcher調(diào)度器對象自身維護的ExecutorService線程池對象中按順序執(zhí)行的辐马。不同網(wǎng)絡(luò)任務(wù)的執(zhí)行狀態(tài)又分別保存在Dispatcher調(diào)度器對象自身維護的3個不同的雙端阻塞隊列中拷橘。
下面終于到了OkHttp原理的重點和難點部分了,OkHttp的內(nèi)部實現(xiàn)通過一個責(zé)任鏈模式完成,將網(wǎng)絡(luò)請求的各個階段封裝到各個鏈條中冗疮,實現(xiàn)了各層的解耦萄唇。而這個責(zé)任鏈模式的具體實現(xiàn)就在getResponseWithInterceptorChain()方法中。
getResponseWithInterceptorChain方法(OKHTTP責(zé)任鏈模式的實現(xiàn))
getResponseWithInterceptorChain方法是整個OkHttp實現(xiàn)責(zé)任鏈模式的核心术幔。下面來看看其源碼:
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
//創(chuàng)建攔截器數(shù)組
val interceptors = mutableListOf<Interceptor>()
//添加應(yīng)用攔截器
interceptors += client.interceptors
//添加重試和重定向攔截器
interceptors += RetryAndFollowUpInterceptor(client)
//添加橋接攔截器
interceptors += BridgeInterceptor(client.cookieJar)
//添加緩存攔截器
interceptors += CacheInterceptor(client.cache)
//添加連接攔截器
interceptors += ConnectInterceptor
if (!forWebSocket) {
//添加網(wǎng)絡(luò)攔截器
interceptors += client.networkInterceptors
}
//添加請求攔截器
interceptors += CallServerInterceptor(forWebSocket)
//創(chuàng)建責(zé)任鏈
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 {
//啟動責(zé)任鏈
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)
}
}
}
這里我們先不關(guān)心每個攔截器具體作用是什么另萤,主流程最終走到chain.proceed(originalRequest)。我們先來看一下這個procceed方法:
@Throws(IOException::class)
override fun proceed(request: Request): Response {
check(index < interceptors.size)
// 統(tǒng)計當前攔截器調(diào)用proceed方法的次數(shù)
calls++
if (exchange != null) {
// exchage是對請求流的封裝诅挑,在執(zhí)行ConnectInterceptor前為空四敞,連接和流已經(jīng)建立但此時此連接不再支持當前url說明之前的網(wǎng)絡(luò)攔截器對url或端口進行了修改,這是不允許的!!
check(exchange.connection.supportsUrl(request.url)) {
"network interceptor ${interceptors[index - 1]} must retain the same host and port"
}
// 這里是對攔截器調(diào)用proceed方法的限制拔妥,在ConnectInterceptor及其之后的攔截器最多只能調(diào)用一次proceed!!
check(calls == 1) {
"network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
}
}
// 創(chuàng)建下一層責(zé)任鏈 注意index + 1
val next = copy(index = index + 1, request = request)
//取出下標為index的攔截器忿危,并調(diào)用其intercept方法,將新建的鏈傳入没龙。
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
if (exchange != null) {
// 保證在ConnectInterceptor及其之后的攔截器至少調(diào)用一次proceed!!
check(index + 1 >= interceptors.size || next.calls == 1) {
"network interceptor $interceptor must call proceed() exactly once"
}
}
//保證未返回含有空body值的response
check(response.body != null) { "interceptor $interceptor returned a response with no body" }
return response
}
代碼中的注釋已經(jīng)寫得比較清楚了铺厨,總結(jié)起來就是創(chuàng)建下一級責(zé)任鏈,然后取出當前攔截器硬纤,調(diào)用其intercept方法并傳入創(chuàng)建的責(zé)任鏈解滓。++為保證責(zé)任鏈能依次進行下去,必須保證除最后一個攔截器(CallServerInterceptor)外咬摇,其他所有攔截器intercept方法內(nèi)部必須調(diào)用一次chain.proceed()方法++伐蒂,如此一來整個責(zé)任鏈就運行起來了。
比如ConnectInterceptor源碼中:
object ConnectInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
//創(chuàng)建連接和流
val realChain = chain as RealInterceptorChain
val exchange = realChain.call.initExchange(chain)
val connectedChain = realChain.copy(exchange = exchange)
//執(zhí)行下一級責(zé)任鏈
return connectedChain.proceed(realChain.request)
}
}
除此之外在責(zé)任鏈不同節(jié)點對于proceed的調(diào)用次數(shù)有不同的限制肛鹏,ConnectInterceptor攔截器及其之后的攔截器能且只能調(diào)用一次逸邦,因為網(wǎng)絡(luò)握手、連接在扰、發(fā)送請求的工作發(fā)生在這些攔截器內(nèi)缕减,表示正式發(fā)出了一次網(wǎng)絡(luò)請求;而在這之前的攔截器可以執(zhí)行多次proceed芒珠,比如錯誤重試桥狡。
經(jīng)過責(zé)任鏈一級一級的遞推下去,最終會執(zhí)行到CallServerInterceptor的intercept方法皱卓,此方法會將網(wǎng)絡(luò)響應(yīng)的結(jié)果封裝成一個Response對象并return裹芝。之后沿著責(zé)任鏈一級一級的回溯,最終就回到getResponseWithInterceptorChain方法的返回娜汁。
攔截器分類
大致總結(jié)責(zé)任鏈的各個節(jié)點攔截器的作用:
攔截器 | 作用 |
---|---|
應(yīng)用攔截器 | 拿到的是原始請求嫂易,可以添加一些自定義header、通用參數(shù)掐禁、參數(shù)加密怜械、網(wǎng)關(guān)接入等等颅和。 |
RetryAndFollowUpInterceptor | 處理錯誤重試和重定向 |
BridgeInterceptor | 應(yīng)用層和網(wǎng)絡(luò)層的橋接攔截器,主要工作是為請求添加cookie缕允、添加固定的header峡扩,比如Host、Content-Length障本、Content-Type教届、User-Agent等等,然后保存響應(yīng)結(jié)果的cookie彼绷,如果響應(yīng)使用gzip壓縮過巍佑,則還需要進行解壓茴迁。 |
CacheInterceptor | 緩存攔截器寄悯,如果命中緩存則不會發(fā)起網(wǎng)絡(luò)請求。 |
ConnectInterceptor | 連接攔截器堕义,內(nèi)部會維護一個連接池猜旬,負責(zé)連接復(fù)用、創(chuàng)建連接(三次握手等等)倦卖、釋放連接以及創(chuàng)建連接上的socket流洒擦。 |
networkInterceptors(網(wǎng)絡(luò)攔截器) | 用戶自定義攔截器,通常用于監(jiān)控網(wǎng)絡(luò)層的數(shù)據(jù)傳輸怕膛。 |
CallServerInterceptor | 請求攔截器熟嫩,在前置準備工作完成后,真正發(fā)起了網(wǎng)絡(luò)請求褐捻。 |
參考文章
https://blog.csdn.net/qq_29152241/article/details/82011539
http://www.reibang.com/p/8bcfb10243a1