Android開發(fā)OkHttp執(zhí)行流程源碼分析
前言
OkHttp
是一套處理 HTTP
網(wǎng)絡(luò)請求的依賴庫,由 Square
公司設(shè)計(jì)研發(fā)并開源,目前可以在 Java
和 Kotlin
中使用。
對于 Android App
來說,OkHttp
現(xiàn)在幾乎已經(jīng)占據(jù)了所有的網(wǎng)絡(luò)請求操作讓,我們了解其內(nèi)部實(shí)現(xiàn)原理可以更好地進(jìn)行功能擴(kuò)展、封裝以及優(yōu)化。
本文基于OkHttp 4.11.0
進(jìn)行分析
OkHttp
的具體使用可以參考官網(wǎng),這里不做具體的說明,本文主要從OkHttp
的使用入手,來具體分析OkHttp
的實(shí)現(xiàn)原理。
介紹
OkHttp是通過socket和okio進(jìn)行交換數(shù)據(jù)的
val client = OkHttpClient() val request = Request.Builder().get().url("http://xxx").build() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { } override fun onResponse(call: Call, response: Response) { } })
從上面我們可以看到幾個(gè)OkHttp
重要的組成部分
- OkHttpClient:
Okhttp
用于請求的執(zhí)行客戶端 - Request: 通過Bulider設(shè)計(jì)模式,構(gòu)建的一個(gè)請求對象
- Call: 是通過
client.newCall
生成的請求執(zhí)行對象,當(dāng)執(zhí)行了execute
之后才會(huì)真正的開始執(zhí)行網(wǎng)絡(luò)請求 - Response: 是通過網(wǎng)絡(luò)請求后,從服務(wù)器返回的信息都在里面。內(nèi)含返回的狀態(tài)碼,以及代表響應(yīng)消息正文的
ResponseBody
- interceptor 用戶定義的攔截器,在重試攔截器之前執(zhí)行
- retryAndFollowUpInterceptor 重試攔截器
- BridgeInterceptor 建立網(wǎng)絡(luò)橋梁的攔截器,主要是為了給網(wǎng)絡(luò)請求時(shí)候,添加各種各種必要參數(shù)。如Cookie,Content-type
- CacheInterceptor 緩存攔截器,主要是為了在網(wǎng)絡(luò)請求時(shí)候,根據(jù)返回碼處理緩存。
- ConnectInterceptor 連接攔截器,主要是為了從連接池子中查找可以復(fù)用的socket連接。
- networkInterceptors 用戶定義的網(wǎng)絡(luò)攔截器,在CallServerInterceptor(執(zhí)行網(wǎng)絡(luò)請求攔截器)之前運(yùn)行。
- CallServerInterceptor 真正執(zhí)行網(wǎng)絡(luò)請求的邏輯。
執(zhí)行流程
OkHttpClient
class Builder constructor() { //Okhttp 請求分發(fā)器,是整個(gè)OkhttpClient的執(zhí)行核心 internal var dispatcher: Dispatcher = Dispatcher() //Okhttp連接池,不過會(huì)把任務(wù)委托給RealConnectionPool處理 internal var connectionPool: ConnectionPool = ConnectionPool() //用戶定義的攔截器,在重試攔截器之前執(zhí)行 internal val interceptors: MutableList<Interceptor> = mutableListOf() //用戶定義的網(wǎng)絡(luò)攔截器,在CallServerInterceptor(執(zhí)行網(wǎng)絡(luò)請求攔截器)之前運(yùn)行。 internal val networkInterceptors: MutableList<Interceptor> = mutableListOf() //流程監(jiān)聽器 internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory() //連接失敗時(shí)是否重連 internal var retryOnConnectionFailure = true //服務(wù)器認(rèn)證設(shè)置 internal var authenticator: Authenticator = Authenticator.NONE //是否重定向 internal var followRedirects = true //是否重定向到https internal var followSslRedirects = true //cookie持久化的設(shè)置 internal var cookieJar: CookieJar = CookieJar.NO_COOKIES //緩存設(shè)置 internal var cache: Cache? = null //DNS設(shè)置 internal var dns: Dns = Dns.SYSTEM //代理設(shè)置 internal var proxy: Proxy? = null internal var proxySelector: ProxySelector? = null internal var proxyAuthenticator: Authenticator = Authenticator.NONE //默認(rèn)的socket連接池 internal var socketFactory: SocketFactory = SocketFactory.getDefault() //用于https的socket連接池 internal var sslSocketFactoryOrNull: SSLSocketFactory? = null //用于信任Https證書的對象 internal var x509TrustManagerOrNull: X509TrustManager? = null internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS //http協(xié)議集合 internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS //https對host的檢驗(yàn) internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT internal var certificateChainCleaner: CertificateChainCleaner? = null //請求超時(shí) internal var callTimeout = 0 //連接超時(shí) internal var connectTimeout = 10_000 //讀取超時(shí) internal var readTimeout = 10_000 //寫入超時(shí) internal var writeTimeout = 10_000 internal var pingInterval = 0 internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE internal var routeDatabase: RouteDatabase? = null }
client.newCall(request):
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
在這里生成一個(gè)RealCall對象,這里第三個(gè)參數(shù)是否為websocket,默認(rèn)是false。 在拿到RealCall對象之后,這里有兩種方式起發(fā)送網(wǎng)絡(luò)請求:
- execute() : 這種方式很少用
- enqueue() : 這種方式是將每個(gè)請求放在隊(duì)列中,按照順序逐個(gè)去進(jìn)行消費(fèi)。
RealCall.enqueue()
override fun enqueue(responseCallback: Callback) { check(executed.compareAndSet(false, true)) { "Already Executed" } callStart() client.dispatcher.enqueue(AsyncCall(responseCallback)) } private fun callStart() { this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()") eventListener.callStart(this) }
這里主要做了一下幾步
- 首先回調(diào)eventListener的callStart()方法,
- 然后把創(chuàng)建AsyncCall對象將responseCallback傳進(jìn)去。
- 最后Dispatcher的enqueue()方法.
Dispatcher.enqueue()
class Dispatcher constructor() { ...... //按運(yùn)行順序準(zhǔn)備異步調(diào)用的隊(duì)列 private val readyAsyncCalls = ArrayDeque<AsyncCall>() //正在運(yùn)行的異步請求隊(duì)列, 包含取消但是還未finish的AsyncCall private val runningAsyncCalls = ArrayDeque<AsyncCall>() //正在運(yùn)行的同步請求隊(duì)列, 包含取消但是還未finish的RealCall private val runningSyncCalls = ArrayDeque<RealCall>() ...... internal fun enqueue(call: AsyncCall) { synchronized(this) { readyAsyncCalls.add(call) if (!call.call.forWebSocket) { val existingCall = findExistingCallWithHost(call.host) if (existingCall != null) call.reuseCallsPerHostFrom(existingCall) } } promoteAndExecute() } 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 }
- 首先將AsyncCall加入readyAsyncCalls隊(duì)列中.
- 然后通過findExistingCallWithHost查找在runningAsyncCalls和readyAsyncCalls是否存在相同host的AsyncCall,如果存在則調(diào)用call.reuseCallsPerHostFrom()進(jìn)行復(fù)用
- 最后調(diào)用 promoteAndExecute() 通過線程池執(zhí)行隊(duì)列中的AsyncCall對象
private fun promoteAndExecute(): Boolean { this.assertThreadDoesntHoldLock() val executableCalls = mutableListOf<AsyncCall>() //判斷是否有請求正在執(zhí)行 val isRunning: Boolean //加鎖,保證線程安全 synchronized(this) { //遍歷 readyAsyncCalls 隊(duì)列 val i = readyAsyncCalls.iterator() while (i.hasNext()) { val asyncCall = i.next() //runningAsyncCalls的數(shù)量不能大于最大并發(fā)請求數(shù) 64 if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity. //同一Host的最大數(shù)是5 if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity. //從readyAsyncCalls隊(duì)列中移除并加入到executableCalls和runningAsyncCalls中 i.remove() asyncCall.callsPerHost.incrementAndGet() executableCalls.add(asyncCall) runningAsyncCalls.add(asyncCall) } isRunning = runningCallsCount() > 0 } //遍歷executableCalls 執(zhí)行asyncCall for (i in 0 until executableCalls.size) { val asyncCall = executableCalls[i] asyncCall.executeOn(executorService) } return isRunning }
在這里遍歷readyAsyncCalls隊(duì)列,判斷runningAsyncCalls的數(shù)量是否大于最大并發(fā)請求數(shù)64, 判斷同一Host的請求是否大于5,然后將AsyncCall從readyAsyncCalls隊(duì)列中移除,并加入到executableCalls和runningAsyncCalls中,遍歷executableCalls 執(zhí)行asyncCall.
internal inner class AsyncCall( private val responseCallback: Callback ) : Runnable { ...... fun executeOn(executorService: ExecutorService) { client.dispatcher.assertThreadDoesntHoldLock() var success = false try { //執(zhí)行AsyncCall 的run方法 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 { //執(zhí)行OkHttp的攔截器 獲取response對象 val response = getResponseWithInterceptorChain() signalledCallback = true //通過該方法將response對象回調(diào)出去 responseCallback.onResponse(this@RealCall, response) } catch (e: IOException) { if (signalledCallback) { Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e) } else { //遇到IO異常 回調(diào)失敗方法 responseCallback.onFailure(this@RealCall, e) } } catch (t: Throwable) { //遇到其他異常 回調(diào)失敗方法 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) } } } }
這里可以看到AsyncCall就是一個(gè)Runable對象,線程執(zhí)行就會(huì)調(diào)用該對象的run方法,而executeOn方法就是執(zhí)行runable對象. 在run方法中主要執(zhí)行了以下幾步:
- 調(diào)用getResponseWithInterceptorChain()執(zhí)行OkHttp攔截器,獲取response對象
- 調(diào)用responseCallback的onResponse方法將Response對象回調(diào)出去
- 如果遇見IOException異常則調(diào)用responseCallback的onFailure方法將異?;卣{(diào)出去
- 如果遇到其他異常,調(diào)用cancel()方法取消請求,調(diào)用responseCallback的onFailure方法將異?;卣{(diào)出去
- 調(diào)用Dispatcher的finished方法結(jié)束執(zhí)行
@Throws(IOException::class) internal fun getResponseWithInterceptorChain(): Response { // 攔截器集合 val interceptors = mutableListOf<Interceptor>() //添加用戶自定義集合 interceptors += client.interceptors interceptors += RetryAndFollowUpInterceptor(client) interceptors += BridgeInterceptor(client.cookieJar) interceptors += CacheInterceptor(client.cache) interceptors += ConnectInterceptor //如果不是sockect 添加newtwork攔截器 if (!forWebSocket) { interceptors += client.networkInterceptors } interceptors += CallServerInterceptor(forWebSocket) //構(gòu)建攔截器責(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 { //執(zhí)行攔截器責(zé)任鏈獲取Response 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í)行了以下幾步操作
- 首先構(gòu)建一個(gè)可變interceptor集合,將所有攔截器添加進(jìn)去,這里如果是websocket則不添加networkInterceptor攔截器,這個(gè)interceptor集合的添加順序也就是OkHttp攔截器的執(zhí)行順序
- 構(gòu)建一個(gè)RealInterceptorChain對象,將所有的攔截器包裹
- 調(diào)用RealInterceptorChain的proceed的方法,獲得Response對象
簡單的總結(jié)一下:這里才用了責(zé)任鏈設(shè)計(jì)模式,構(gòu)建RealInterceptorChain對象,然后執(zhí)行proceed方法獲取response對象
fun interface Interceptor { //攔截方法 @Throws(IOException::class) fun intercept(chain: Chain): Response companion object { inline operator fun invoke(crossinline block: (chain: Chain) -> Response): Interceptor = Interceptor { block(it) } } interface Chain { //獲取Request對象 fun request(): Request //處理請求獲取Reponse @Throws(IOException::class) fun proceed(request: Request): Response ...... } }
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 ) : Interceptor.Chain { internal fun copy( index: Int = this.index, exchange: Exchange? = this.exchange, request: Request = this.request, connectTimeoutMillis: Int = this.connectTimeoutMillis, readTimeoutMillis: Int = this.readTimeoutMillis, writeTimeoutMillis: Int = this.writeTimeoutMillis ) = RealInterceptorChain(call, interceptors, index, exchange, request, connectTimeoutMillis, readTimeoutMillis, writeTimeoutMillis) ...... override fun call(): Call = call override fun request(): Request = request @Throws(IOException::class) override fun proceed(request: Request): Response { check(index < interceptors.size) ...... 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") ...... return response } }
這里看一看到copy()方法就是創(chuàng)建了一個(gè)RealInterceptorChain()對象,不過需要注意的是index在創(chuàng)建對象時(shí)是index = index + 1,這樣就會(huì)執(zhí)行index對應(yīng)下標(biāo)的攔截器,不斷的調(diào)用下一個(gè)攔截器,直到有response對象返回,也就是chain.proceed(originalRequest)結(jié)束。
Interceptor
下面我們來具體分析一下攔截器
RetryAndFollowUpInterceptor
主要處理了如下幾個(gè)方向的問題:
- 1.異常,或者協(xié)議重試(408客戶端超時(shí),權(quán)限問題,503服務(wù)暫時(shí)不處理,retry-after為0)
- 2.重定向
- 3.重試的次數(shù)不能超過20次。
@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) { //這里會(huì)新建一個(gè)ExchangeFinder,ConnectInterceptor會(huì)使用到 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) { //嘗試通過路由連接失敗。該請求將不會(huì)被發(fā)送。 if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) { throw e.firstConnectException.withSuppressed(recoveredFailures) } else { recoveredFailures += e.firstConnectException } newExchangeFinder = false continue } catch (e: IOException) { //嘗試與服務(wù)器通信失敗。該請求可能已發(fā)送。 if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) { throw e.withSuppressed(recoveredFailures) } else { recoveredFailures += e } newExchangeFinder = false continue } //嘗試關(guān)聯(lián)上一個(gè)response,注意:body是為null if (priorResponse != null) { response = response.newBuilder() .priorResponse(priorResponse.newBuilder() .body(null) .build()) .build() } val exchange = call.interceptorScopedExchange //會(huì)根據(jù) responseCode 來判斷,構(gòu)建一個(gè)新的request并返回來重試或者重定向 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() //最大重試次數(shù),不同的瀏覽器是不同的,比如:Chrome為21,Safari則是16 if (++followUpCount > MAX_FOLLOW_UPS) { throw ProtocolException("Too many follow-up requests: $followUpCount") } request = followUp priorResponse = response } finally { call.exitNetworkInterceptorExchange(closeActiveExchange) } } }
- 1.調(diào)用RealCall的enterNetworkInterceptorExchange方法實(shí)例化一個(gè)
ExchangeFinder
在RealCall對象中。 - 2.執(zhí)行RealCall的proceed 方法,進(jìn)入下一個(gè)攔截器,進(jìn)行下一步的請求處理。
- 3.如果出現(xiàn)路由異常,則通過recover方法校驗(yàn),當(dāng)前的連接是否可以重試,不能重試則拋出異常,離開當(dāng)前的循環(huán)。
private fun recover( e: IOException, call: RealCall, userRequest: Request, requestSendStarted: Boolean ): Boolean { //禁止重連 if (!client.retryOnConnectionFailure) return false // 不能再次發(fā)送請求體 if (requestSendStarted && requestIsOneShot(e, userRequest)) return false // 致命異常 if (!isRecoverable(e, requestSendStarted)) return false // 沒有更多線路可以重連 if (!call.retryAfterFailure()) return false // 對于故障恢復(fù),將相同的路由選擇器與新連接一起使用 return true }
BridgeInterceptor
主要處理了如下幾個(gè)問題:
- 主要將Content-Type、Content-Length、Host等一些數(shù)據(jù)添加到頭部。
- 拿到數(shù)據(jù)之后對數(shù)據(jù)進(jìn)行處理,判斷是否為gzip,進(jìn)行對數(shù)據(jù)數(shù)據(jù)解壓。
@Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { //獲取原始請求數(shù)據(jù) val userRequest = chain.request() val requestBuilder = userRequest.newBuilder() //重新構(gòu)建請求 添加一些必要的請求頭信息 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") } 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) } //執(zhí)行下一個(gè)攔截器 val networkResponse = chain.proceed(requestBuilder.build()) cookieJar.receiveHeaders(userRequest.url, networkResponse.headers) //創(chuàng)建一個(gè)新的responseBuilder,目的是將原始請求數(shù)據(jù)構(gòu)建到response中 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() //修改response header信息,移除Content-Encoding,Content-Length信息 responseBuilder.headers(strippedHeaders) val contentType = networkResponse.header("Content-Type" //修改response body信息 responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer())) } } return responseBuilder.build() }
- 設(shè)置頭部的Content-Type.說明內(nèi)容類型是什么
- 如果contentLength大于等于0,則設(shè)置頭部的Content-Length(說明內(nèi)容大小是多少);否則設(shè)置頭部的Transfer-Encoding為chunked(說明傳輸編碼為分塊傳輸)
- 如果Host不存在,設(shè)置頭部的Host(在Http 1.1之后出現(xiàn),可以通過同一個(gè)URL訪問到不同主機(jī),從而實(shí)現(xiàn)服務(wù)器虛擬服務(wù)器的負(fù)載均衡。如果1.1之后不設(shè)置就會(huì)返回404)。
- 如果Connection不存在,設(shè)置頭部的Connection為Keep-Alive(代表連接狀態(tài)需要保持活躍)
- 如果Accept-Encoding且Range為空,則強(qiáng)制設(shè)置Accept-Encoding為gzip(說明請求將會(huì)以gzip方式壓縮)
- 從CookieJar的緩存中取出cookie設(shè)置到頭部的Cookie
- 如果User-Agent為空,則設(shè)置User-Agent到頭部
CacheInterceptor
用戶通過OkHttpClient.cache
來配置緩存,緩存攔截器通過CacheStrategy
來判斷是使用網(wǎng)絡(luò)還是緩存來構(gòu)建response
。
@Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { val call = chain.call() //通過request從OkHttpClient.cache中獲取緩存 val cacheCandidate = cache?.get(chain.request()) val now = System.currentTimeMillis() //創(chuàng)建緩存策略 val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute() //為空表示不使用網(wǎng)絡(luò),反之,則表示使用網(wǎng)絡(luò) val networkRequest = strategy.networkRequest //為空表示不使用緩存,反之,則表示使用緩存 val cacheResponse = strategy.cacheResponse //追蹤網(wǎng)絡(luò)與緩存的使用情況 cache?.trackResponse(strategy) val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE //有緩存但不適用,關(guān)閉它 if (cacheCandidate != null && cacheResponse == null) { cacheCandidate.body?.closeQuietly() } //如果網(wǎng)絡(luò)被禁止,但是緩存又是空的,構(gòu)建一個(gè)code為504的response,并返回 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) } } //如果我們禁用了網(wǎng)絡(luò)不使用網(wǎng)絡(luò),且有緩存,直接根據(jù)緩存內(nèi)容構(gòu)建并返回response if (networkRequest == null) { return cacheResponse!!.newBuilder() .cacheResponse(stripBody(cacheResponse)) .build().also { listener.cacheHit(call, it) } } //為緩存添加監(jiān)聽 if (cacheResponse != null) { listener.cacheConditionalHit(call, cacheResponse) } else if (cache != null) { listener.cacheMiss(call) } var networkResponse: Response? = null try { //執(zhí)行下一個(gè)攔截器 networkResponse = chain.proceed(networkRequest) } finally { //捕獲I/O或其他異常,請求失敗,networkResponse為空,且有緩存的時(shí)候,不暴露緩存內(nèi)容 if (networkResponse == null && cacheCandidate != null) { //否則關(guān)閉緩存響應(yīng)體 cacheCandidate.body?.closeQuietly() } } //如果有緩存 if (cacheResponse != null) { //且網(wǎng)絡(luò)返回response code為304的時(shí)候,使用緩存內(nèi)容新構(gòu)建一個(gè)Response返回。 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() cache!!.trackConditionalCacheHit() cache.update(cacheResponse, response) return response.also { listener.cacheHit(call, it) } } else { //否則關(guān)閉緩存響應(yīng)體 cacheResponse.body?.closeQuietly() } } //構(gòu)建網(wǎng)絡(luò)請求的response val response = networkResponse!!.newBuilder() .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build() //如果cache不為null,即用戶在OkHttpClient中配置了緩存,則將上一步新構(gòu)建的網(wǎng)絡(luò)請求response存到cache中 if (cache != null) { //根據(jù)response的code,header以及CacheControl.noStore來判斷是否可以緩存 if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) { // 將該response存入緩存 val cacheRequest = cache.put(response) return cacheWritingResponse(cacheRequest, response).also { if (cacheResponse != null) { listener.cacheMiss(call) } } } //根據(jù)請求方法來判斷緩存是否有效,只對Get請求進(jìn)行緩存,其它方法的請求則移除 if (HttpMethod.invalidatesCache(networkRequest.method)) { try { //緩存無效,將該請求緩存從client緩存配置中移除 cache.remove(networkRequest) } catch (_: IOException) { } } } return response }
網(wǎng)絡(luò)請求前:
- 首先根據(jù)request從OkHttpClient.cache中獲取緩存,通過
CacheStrategy
獲取本次請求的請求體及緩存的響應(yīng)體。 - 如果 請求體
networkRequest
和響應(yīng)體cacheResponse
都為空的話,則返回錯(cuò)誤碼為 504 - 如果 請求體
networkRequest
為空 響應(yīng)體cacheResponse
不為空的話,則將該響應(yīng)體返回 - 如果請求體
networkRequest
不為空的話,則進(jìn)入下一個(gè)攔截器。
網(wǎng)絡(luò)請求后:
- 如果當(dāng)前
cacheResponse
不為空,且networkResponse
狀態(tài)碼為304, 則代表數(shù)據(jù)沒有變化,那么就會(huì)根據(jù)cacheResponse
構(gòu)建一個(gè)新的response
,根據(jù)當(dāng)前時(shí)間更新到緩存當(dāng)中,并返回到上一攔截器中 - 如果
networkResponse
狀態(tài)碼不為304,則判斷是否進(jìn)行緩存,最后返回到上一攔截器中
從LruCache中獲取緩存
val cacheCandidate = cache?.get(chain.request())
internal fun get(request: Request): Response? { val key = key(request.url) val snapshot: DiskLruCache.Snapshot = try { cache[key] ?: return null } catch (_: IOException) { return null // Give up because the cache cannot be read. } val entry: Entry = try { Entry(snapshot.getSource(ENTRY_METADATA)) } catch (_: IOException) { snapshot.closeQuietly() return null } val response = entry.response(snapshot) if (!entry.matches(request, response)) { response.body?.closeQuietly() return null } return response }
@JvmStatic fun key(url: HttpUrl): String = url.toString().encodeUtf8().md5().hex()
- 首先將url轉(zhuǎn)化為urf-8,并且通過md5拿到摘要,再調(diào)用hex獲取16進(jìn)制的字符串,該字符串就是LruCache的key;
- 通過key獲取到
DiskLruCache.Snapshot
對象(這里在DiskLruCache
中重寫了get方法),根據(jù)DiskLruCache.Snapshot
對象獲取到okio 的source。
DiskLruCache:
@Synchronized @Throws(IOException::class) operator fun get(key: String): Snapshot? { initialize() checkNotClosed() validateKey(key) val entry = lruEntries[key] ?: return null val snapshot = entry.snapshot() ?: return null redundantOpCount++ journalWriter!!.writeUtf8(READ) .writeByte(' '.toInt()) .writeUtf8(key) .writeByte('\n'.toInt()) if (journalRebuildRequired()) { cleanupQueue.schedule(cleanupTask) } return snapshot }
- 最后將數(shù)據(jù)轉(zhuǎn)化為響應(yīng)體
再來看看那些響應(yīng)體需要緩存:
這里是網(wǎng)絡(luò)請求回來,判斷是否需要緩存的處理
if (cache != null) { if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) { val cacheRequest = cache.put(response) return cacheWritingResponse(cacheRequest, response).also { if (cacheResponse != null) { listener.cacheMiss(call) } } } if (HttpMethod.invalidatesCache(networkRequest.method)) { try { cache.remove(networkRequest) } catch (_: IOException) { } } }
- 首先根據(jù)cache對象是否為空,決定是否進(jìn)入緩存判斷
response.promisesBody()
判斷響應(yīng)體是否有正文,CacheStrategy.isCacheable(response, networkRequest)
這里是判斷哪些狀態(tài)碼需要緩存- 這里
HttpMethod.invalidatesCache(networkRequest.method)
判斷哪些請求方式是否為POST
、PATCH
、PUT
、DELETE
、MOVE
,如果為true的話則移除緩存。
fun isCacheable(response: Response, request: Request): Boolean { when (response.code) { HTTP_OK, HTTP_NOT_AUTHORITATIVE, HTTP_NO_CONTENT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_NOT_FOUND, HTTP_BAD_METHOD, HTTP_GONE, HTTP_REQ_TOO_LONG, HTTP_NOT_IMPLEMENTED, StatusLine.HTTP_PERM_REDIRECT -> { } HTTP_MOVED_TEMP, StatusLine.HTTP_TEMP_REDIRECT -> { if (response.header("Expires") == null && response.cacheControl.maxAgeSeconds == -1 && !response.cacheControl.isPublic && !response.cacheControl.isPrivate) { return false } } else -> { return false } } return !response.cacheControl.noStore && !request.cacheControl.noStore }
如果狀態(tài)碼為200
、203
、204
、301
、404
、405
、410
、414
、501
、308
都可以緩存,其他則返回false
不進(jìn)行緩存
以上就是Android開發(fā)OkHttp執(zhí)行流程源碼分析的詳細(xì)內(nèi)容,更多關(guān)于Android OkHttp執(zhí)行流程的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解Android短信的發(fā)送和廣播接收實(shí)現(xiàn)短信的監(jiān)聽
本篇文章主要介紹了Android短信的發(fā)送和廣播接收實(shí)現(xiàn)短信的監(jiān)聽,可以實(shí)現(xiàn)短信收發(fā),有興趣的可以了解一下。2016-11-11Android 中SwipeRefreshLayout與ViewPager滑動(dòng)事件沖突解決方法
這篇文章主要介紹了Android 中SwipeRefreshLayout與ViewPager滑動(dòng)事件沖突解決方法的相關(guān)資料,需要的朋友可以參考下2017-04-04Android notifyDataSetChanged() 動(dòng)態(tài)更新ListView案例詳解
這篇文章主要介紹了Android notifyDataSetChanged() 動(dòng)態(tài)更新ListView案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08詳談Android動(dòng)畫效果translate、scale、alpha、rotate
下面小編就為大家?guī)硪黄斦凙ndroid動(dòng)畫效果translate、scale、alpha、rotate。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01android listview進(jìn)階實(shí)例分享
這篇文章主要介紹了android listview進(jìn)階實(shí)例分享,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01Android生成隨機(jī)數(shù)的方法實(shí)例
這篇文章主要為大家詳細(xì)介紹了Android生成隨機(jī)數(shù)的方法實(shí)例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03