上一篇文章中咱們簡單的總結了ok發起請求並接收響應的整個流程,咱們發現關鍵步驟是getResponseWithInterceptorChain這個方法。那這裏面又作了什麼事情呢?仍是首先來看一下代碼:設計模式
@Throws(IOException::class)
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(interceptors, transmitter, null, 0, originalRequest, this,
client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis)
var calledNoMoreExchanges = false
try {
val response = chain.proceed(originalRequest)
if (transmitter.isCanceled) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw transmitter.noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null)
}
}
}
複製代碼
能夠說這個方法是整個okhttp的核心,經過這麼幾行代碼就實現了整個請求的過程。這裏不得不提到ok的攔截器機制。大多數人說到okhttp優秀的地方基本上都會脫口而出經過設置攔截器能夠很方便的實現網絡請求的打印、添加一些公共參數等等。而攔截器真正的運行其實就是在這個方法中。緩存
說了這麼久的攔截器,確定會好奇它到底是個什麼妖魔鬼怪:服務器
/** * Observes, modifies, and potentially short-circuits requests going out and the corresponding * responses coming back in. Typically interceptors add, remove, or transform headers on the request * or response. */
interface Interceptor {
@Throws(IOException::class)
fun intercept(chain: Chain): Response
companion object {
inline operator fun invoke(crossinline block: (chain: Chain) -> Response): Interceptor =
object : Interceptor {
override fun intercept(chain: Chain) = block(chain)
}
}
interface Chain {
fun request(): Request
@Throws(IOException::class)
fun proceed(request: Request): Response
fun connection(): Connection?
fun call(): Call
fun connectTimeoutMillis(): Int
fun withConnectTimeout(timeout: Int, unit: TimeUnit): Chain
fun readTimeoutMillis(): Int
fun withReadTimeout(timeout: Int, unit: TimeUnit): Chain
fun writeTimeoutMillis(): Int
fun withWriteTimeout(timeout: Int, unit: TimeUnit): Chain
}
}
複製代碼
Interceptor其實是一個接口,裏面定義了一個intercept方法和chain的接口類。cookie
關於intercept方法是否是很熟悉?咱們在自定義攔截器的時候每每都是在這裏面作一些瞎操做。網絡
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val realInterceptorChain = chain as RealInterceptorChain
val transmitter = realInterceptorChain.transmitter()
val response = realInterceptorChain.proceed(request, transmitter, null)
return response
}
複製代碼
這裏實際上是將每一個攔截器分爲了兩個階段:ide
攔截器的設計實際上是採用了遞歸調用的思想,仔細想一想實際上是很是巧妙的,這樣能夠保證不管是系統內置仍是用戶自定義的攔截器均可以獲得最終執行。若是在某個攔截器中出現了錯誤,還能夠把錯誤拋給上一層來處理。學習
經過剖析源碼咱們會發現,ok內部實際上是幫咱們內置了好幾種攔截器,每次請求都會添加進去,咱們按照添加順序整理以下:ui
攔截器 | 說明 |
---|---|
RetryAndFollowUpInterceptor | 負責實現重定向功能 |
BridgeInterceptor | 將用戶構造的請求轉換爲向服務器發送的請求,將服務器返回的響應轉換爲對用戶友好的響應 |
CacheInterceptor | 讀取緩存、更新緩存 |
ConnectInterceptor | 創建與服務器的鏈接 |
CallServerInterceptor | 從服務器讀取響應 |
能夠看出,整個網絡請求的過程是經過各個攔截器相互配合來實現,假如咱們在網絡請求過程當中設置容許緩存,那麼在請求網絡以前會預先獲取緩存,而後再去跟服務器創建鏈接。經過攔截器的機制,咱們能夠很方便的控制整個網絡請求的過程以及添加咱們本身的拓展。this
整個網絡請求過程當中,用戶實際上是能夠添加兩種不一樣類型的攔截器的。url
這一點在getResponseWithInterceptorChain()方法中添加攔截器的順序是能夠分析出來的。
通過上面的分析咱們知道okhttp的實際請求和響應過程是經過一個個攔截器的遞歸調用來實現的。主要方法就是
realInterceptorChain.proceed。咱們先來看一下RealInterceptorChain的構造方法
class RealInterceptorChain(
private val interceptors: List<Interceptor>,
private val transmitter: Transmitter,
private val exchange: Exchange?,
private val index: Int,
private val request: Request,
private val call: Call,
private val connectTimeout: Int,
private val readTimeout: Int,
private val writeTimeout: Int
)
複製代碼
這裏實際上是將上一個攔截器的一些參數傳遞進來而且賦值的簡單實現。包括全部的攔截器、以及咱們在第一篇文章提到的transmitter等。整個類中最關鍵的仍是proceed方法
@Throws(IOException::class)
fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
if (index >= interceptors.size) throw AssertionError()
calls++
// If we already have a stream, confirm that the incoming request will use it.
check(this.exchange == null || this.exchange.connection()!!.supportsUrl(request.url)) {
"network interceptor ${interceptors[index - 1]} must retain the same host and port"
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
check(this.exchange == null || calls <= 1) {
"network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
}
// Call the next interceptor in the chain.
val next = RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
// Confirm that the next interceptor made its required call to chain.proceed().
check(exchange == null || index + 1 >= interceptors.size || next.calls == 1) {
"network interceptor $interceptor must call proceed() exactly once"
}
check(response.body != null) { "interceptor $interceptor returned a response with no body" }
return response
}
複製代碼
能夠看到這個方法中除了大量的判讀邏輯,比較關鍵的就是經過index+1取到下一個RealInterceptorChain對象 。
而後獲取當前的攔截器而且調用其intercept方法 ,方法參數中傳入了下一個攔截器的對應的chain。就是這樣經過遞歸調用的設計,實現了由上而下,再由下而上實現了遞與歸的過程。從而很是漂亮的實現了http請求的全鏈路。
看到這裏有沒有以爲這種設計模式很熟悉,沒錯就是責任鏈模式。這種設計模式在一個流程會有多種操做的業務場景下會很是實用。
本篇文章咱們主要是分析了整個網絡請求過程當中很是重要的getResponseWithInterceptorChain()方法,內部經過責任鏈的模式遞歸調用用戶自定義攔截器和內置攔截器來完成整個網絡請求。經過對整個方法的分析,咱們會更清晰的知道ok內部是怎麼實現經過設置攔截器來完成咱們想要的功能的。同時也是咱們學習責任鏈模式很是好的實踐。後續文章將逐個分析ok內置攔截器的具體做用。