目錄
0.前言
1.OkHttp的簡單使用
2.淺析開始
攔截器
鏈式呼叫流程示意圖
第 0 個攔截器
第一個 攔截器 RetryAndFollowUpInterceptor
第二個攔截器 BridgeInterceptor
第三個攔截器 CacheInterceptor
第四個攔截器 ConnectInterceptor
第五個攔截器
第六個攔截器 CallServerInterceptor
小結
3.結語
0.前言
作為一個已經作業*年的 Android 開發工程師,感覺自己是時候對了解原始碼的內容了(不管什么時候,閱讀原始碼都是很重要的),這次基于閱讀各方的有關學習資料后,自己花了近8個小時,寫出的這篇逐字稿,其中可能還是存在不全面,甚至有錯誤的地方,望諒解,后面會進行二次,三次,甚至多次的修改,也希望大家多多指正,交流~
本篇文章旨在將 OkHttp 做一個請求的流程,在原始碼中是如何實作的進行淺析,
1.OkHttp的簡單使用
可以直接在 官網 參考 https://square.github.io/okhttp/
以下是一個簡單的使用,本文暫時不分析大管家- OkHttpClient
val url = "https://square.github.io/okhttp/"
val client = OkHttpClient()
val request = Request.Builder()
.url(url).build()
try {
client.newCall(request).execute()
} catch (e: Exception) {
Log.i("TAG", "onCreate: Exception---${e.message}")
}
2.淺析開始
首先從最后一行(有意義的一行)入手,最后是呼叫了 execute(),【那么,我們所有的分析都是為了知道 execute 到底做了什么?】
查看后發現是 介面 Call 的一個方法,那么我們就看看呼叫 execute() 的類,那就是 . 前面的東西,
查看后 newCall 是屬于 OkHttpClient 類,然后這個方法
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
接受了一個 request 引數【這個 request 引數就是我們自己創建的 request】,通過 RealCall 回傳一個 Call, 那么我們接著看看 RealCall 是怎么創建一個 Call 的呢?
現在進入到了 RealCall 類
那進來后,發現 RealCall 類實作了 Call 介面,那么回到最開始,我們就是想知道 execute 做了什么?那么我們來看看 RealCall 中的 execute 做了什么吧,(因為 RealCall 不是抽象類,所以exectue 一定有具體實作)
哈哈哈,果不其然,的確有
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)
}
}
到這一步,就將問題轉化為,RealCall 的 execute 做了什么?
直接看 return 的東西,是一個 getResponseWithInterceptorChain() ,那么我們進去看看這個方法做了什么吧?[ 這個方法還是在 RealCall 中]
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)
}
}
}
到這一步,就將問題轉化為,getResponseWithInterceptorChain 做了什么?
依舊先找 return 的東西,發現 return 了個 response,而產生這個 response 是呼叫了 chain.proceed(originalRequest) ,那么我們看看這里的 proceed() 的方法做了什么吧?
override fun proceed(request: Request): Response {
check(index < interceptors.size)
calls++
if (exchange != null) {
check(exchange.finder.sameHostAndPort(request.url)) {
"network interceptor ${interceptors[index - 1]} must retain the same host and port"
}
check(calls == 1) {
"network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
}
}
// Call the next interceptor in the chain.
val next = copy(index = index + 1, request = request)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
if (exchange != null) {
check(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
}
同樣,找最后一行 return,往上追,看到是通過 interceptor.intercept(next) 得到的結果,next 會在上面被+1 重新賦值,這樣就可以把接力棒往下傳了,
嗯?這個 interceptor 是個啥?哪來的?
【在這里打斷一下,反問一下,我們在干嘛?為什么走到這步了?】
哦~原來是我們追溯到 RealCall 的 execute 方法中,然后里面有 getResponseWithInterceptorChain() 然后這個方法中又呼叫了 chain.proceed(originalRequest),于是我們跳到了 RealInterceptorChain 類中查看 proceed 做了什么,這個時候,我們發現這里面呼叫了一個 interceptor.intercept(next) ,于是我們好奇了 interceptor 是哪里來的,以及它的 intercept 做了什么?我們知道這兩個之后,就知道了 RealCall 的 execute 方法做了什么,然后我們就知道 OkHttp 是如何實作一個請求的,(嗯嗯,心滿意足)
首先還是先點擊 intercept 的這個方法,發現,他是一個介面,interceptor 介面 的方法,老規矩,我們接著往上看,看看 具體的 interceptor
class RealInterceptorChain(
internal val call: RealCall,
private val interceptors: List<Interceptor>,
private val index: Int,
internal val exchange: Exchange?,
internal val request: Request,
internal val connectTimeoutMillis: Int,
internal val readTimeoutMillis: Int,
internal val writeTimeoutMillis: Int
)
找到根處,發現這個是我們創建 RealInterceptorChain 實體的時候,傳進來的,那么我們追回上一層 RealCall 中

哦,就是有一個 list 變數,用來裝“一堆” interceptor 的,
攔截器
鏈式呼叫流程示意圖
那么接下來就嘗試把一個個 Interceptor 解讀一遍,在解讀之前,補充一點整個鏈式呼叫流程大概如下圖所示

除了最后一個 interceptor 節點,每一個節點都分為三個作業流程,前置,中置,后置,
【中置作業】將流程推到一下個節點,(通過呼叫 realChain.proceed(request)),
【前置作業】就是本身攔截器要做的一些事情,(例如:尋找可用的鏈接,拿到可用的 Cache 等等)
【后置作業】就是等下個節點把回傳值回傳后的一個作業處理,
有了上面的流程基礎,我們就可以開始分析了,
第 0 個攔截器
client.interceptors //這個是我們自己定義的 interceptors,我們暫時不理會
第一個 攔截器 RetryAndFollowUpInterceptor
/**
* This interceptor recovers from failures and follows redirects as necessary. It may throw an
* [IOException] if the call was canceled.
*/
剛剛我們好奇的就是這些 interceptor 的 intercep 方法做了什么,所以直接看 intercept 方法即可,
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)
}
}
}
有了上面的鋪墊(呼叫流程示意圖),那么我們只需要知道 realChain.proceed(request) 就可以劃分出 前置,后置的作業了,
[//??????????????]處,就是【中置作業】,所以,我們想要【前置作業】做了什么,就往前面看,
【前置作業】call.enterNetworkInterceptorExchange(request, newExchangeFinder)
【后置作業】簡單分析一下,就在嘗試和重定向,直至成功后,回傳拿到的資料 response,并將其回傳,
追溯 enterNetworkInterceptorExchange,發現回到了 RealCall 這個類當中
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
)
}
}
其中上面方法中 newExchangeFinder 為 true,所以將執行 if 代碼塊
這里主要是創建了一個 ExchangeFinder 實體,暫時先不看這個類是什么?因為現在,我們只需要知道他創建了這么一個實體出來,等真正呼叫了對應的方法的時候,我們再去看對應的方法即可,
第二個攔截器 BridgeInterceptor
/**
* Bridges from application code to network code. First it builds a network request from a user
* request. Then it proceeds to call the network. Finally it builds a user response from the network
* response.
*/
通過名字和代碼注釋,這個攔截器,主要是負責 application 和 network 連接的一個連接橋攔截器,
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()
}
同理,找到 ? 所在所在位置,即是分割前置和后置作業的地方,
【前置作業】組裝請求頭
【后置作業】將資料解壓,轉成 response,并將其回傳
第三個攔截器 CacheInterceptor
/** Serves requests from the cache and writes responses to the cache. */
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
}
【前置作業】拿到一個可用的 cache
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
【后置作業】cacheWritingResponse 放回傳來的 cache 存起來,
第四個攔截器 ConnectInterceptor
/**
* Opens a connection to the target server and proceeds to the next interceptor. The network might
* be used for the returned response, or to validate a cached response with a conditional GET.
*/
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)
}
那么在這上面,
可以看到【中置作業】依舊是 proceed
然后【后置作業】就是將其結果回傳,
【前置作業】打開目標服務器的連接,
核心的代碼是第 3 行代碼,那溯源,發現是在 RealCall 的一個方法
/** Finds a new or pooled connection to carry a forthcoming request and response. */
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
}
老規矩,看看最后一行, return 了一個 result,往上看,看到第 12 行代碼生成的,發現其中傳入的一個引數是 exchangeFinder,咦,等等,這個是我們在RetryAndFollowUpInterceptor 這個攔截器生成的那個嗎?是的,因為在 10 行,發現了,就是那個,它被用到了,第 11 行,呼叫了它的 find 方法,我們進去看看
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)
}
}
最后回傳的是一個 ExchangeCodec (/** Encodes HTTP requests and decodes HTTP responses. */)
看 return ,看 newCodec()【 RealCall 】 方法,這里不貼代碼了,就是根據是否是 HTTP2 來生成 HTT2 或者 HTTP1 的編碼解碼器,
那 resultConnection 是什么呢?findHealthyConnection() 生成的
溯源,發現這個在 ExchangeFinder 類中 最后發現是呼叫了 findConnection()
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
}
搜索 (Ctrl + F)return result,發現有三處有關回傳結果,那么我們從第一個開始看起
上面代碼的第40行,
這個 if 代碼塊中,判斷條件,看上去就是從一個連接池中到拿到一個連接,如果拿到了就將這個連接回傳,
溯源 callAcquirePooledConnection() 在 RealConntectionPool 這個類中
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
}
可以看到,就是從所有的連接中,遍歷出一個可以用的連接,如果存在則回傳 true,否則反之,
因為呼叫了三次這個方法,那么我們分析一下對應的引數有什么意義,
address 主要是包含了 host 和 port 的 Address 實體
call 就是 RealCall 的實體
routes 路由串列(可空)
requireMultiplexed 是否使用多路復用(針對HTTP2)
我們先對比三次呼叫的差異
connectionPool.callAcquirePooledConnection(address, call, null, false)
connectionPool.callAcquirePooledConnection(address, call, routes, false)
connectionPool.callAcquirePooledConnection(address, call, routes, true)
得出
只拿不進行[多路復用]的連接
看似好像也不拿[多路復用]的連接,其實不是,這是針對http2的獲取連接,與[多路復用無關],原因:在isEligible 中如果不是HTTP2將會直接回傳false
只拿[多路復用]的連接
那么我們再看看這三次呼叫的時機,
第一次,就是我們正常流程的獲取一個連接,那假如沒有拿到,就會 localRouteselector 中選擇一個組的路由出來,進行第二次的獲取,如果這個時候,還是沒有拿到,那么我們就需要自己創建一個連接了,
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())
創建后,為什么?還是在連接池中拿一次呢?原因是:有兩個請求同時去訪問同一個ip地址,其中 A 已經創建了一個連接,B 也創建了一個連接,這樣就有兩個連接了,造成資源的浪費,所以當 A 創建后,B再創建后,先去連接池里面再拿一次,如果拿到了,就把自己創建的放棄掉(但又不完全放棄,nextRouteToTry 會把剛剛創建的 route 暫存起來,因為有可能真正去用的時候,A創建的那個連接又被釋放了)
那么回歸到這段分析的最開始,就是 exchangeFinder的find() 到底做了什么,那這里我小結一下,就是在連接池中找到一個可用的連接,并且生成對應的編碼解碼器,
然后在回歸到 initExchange 的方法中,最后通過 exchangeFinder 和 codec 實體化了一個Exchange 并回傳,
第五個攔截器
client.networkInterceptors //這個也是自定義的攔截器,暫時跳過
第六個攔截器 CallServerInterceptor
/** This is the last interceptor in the chain.
* It makes a network call to the server. */
在這個類中搜索 proceed,可以看到并沒有這樣的結果,

那么這個攔截器,做的事情,就是把請求發到服務器,并且把服務器回傳的資料,回傳都上一層,
小結
當所有的攔截器都放到了 chain 中,chain 呼叫 proceed ,流程就會像我們上面分析的那樣執行下來,每個攔截器都會做好自己的作業【前置作業】,到最后一個攔截器后,就是有點萬事俱備,只欠東風的感覺了,
3.結語
這次分析,全程沒有打開別的資料,所以可能會存在一些遺漏或者錯誤,歡迎大家勘正,同時,如果這篇文章對你起到了作用,歡迎一鍵三連,點贊,評論,收藏,(老B站選手了),歡迎大家私信交流~~~
本篇文章后續,會進行多次修正或者補充,以及會及時(沒有意外的話,下周末)會發布《OkHttp原始碼解讀逐字稿(2)-OkHttpClient》歡迎大家監督,催更,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/292763.html
標籤:其他
上一篇:iOS—持久化的幾種方案
下一篇:Android性能優化的底層邏輯
