Android開發(fā)OkHttp執(zhí)行流程源碼分析
前言
OkHttp 是一套處理 HTTP 網(wǎng)絡請求的依賴庫,由 Square 公司設計研發(fā)并開源,目前可以在 Java 和 Kotlin 中使用。
對于 Android App 來說,OkHttp 現(xiàn)在幾乎已經(jīng)占據(jù)了所有的網(wǎng)絡請求操作讓,我們了解其內部實現(xiàn)原理可以更好地進行功能擴展、封裝以及優(yōu)化。
本文基于OkHttp 4.11.0 進行分析
OkHttp的具體使用可以參考官網(wǎng),這里不做具體的說明,本文主要從OkHttp的使用入手,來具體分析OkHttp的實現(xiàn)原理。
介紹
OkHttp是通過socket和okio進行交換數(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) {
}
})
從上面我們可以看到幾個OkHttp重要的組成部分
- OkHttpClient:
Okhttp用于請求的執(zhí)行客戶端 - Request: 通過Bulider設計模式,構建的一個請求對象
- Call: 是通過
client.newCall生成的請求執(zhí)行對象,當執(zhí)行了execute之后才會真正的開始執(zhí)行網(wǎng)絡請求 - Response: 是通過網(wǎng)絡請求后,從服務器返回的信息都在里面。內含返回的狀態(tài)碼,以及代表響應消息正文的
ResponseBody
- interceptor 用戶定義的攔截器,在重試攔截器之前執(zhí)行
- retryAndFollowUpInterceptor 重試攔截器
- BridgeInterceptor 建立網(wǎng)絡橋梁的攔截器,主要是為了給網(wǎng)絡請求時候,添加各種各種必要參數(shù)。如Cookie,Content-type
- CacheInterceptor 緩存攔截器,主要是為了在網(wǎng)絡請求時候,根據(jù)返回碼處理緩存。
- ConnectInterceptor 連接攔截器,主要是為了從連接池子中查找可以復用的socket連接。
- networkInterceptors 用戶定義的網(wǎng)絡攔截器,在CallServerInterceptor(執(zhí)行網(wǎng)絡請求攔截器)之前運行。
- CallServerInterceptor 真正執(zhí)行網(wǎng)絡請求的邏輯。
執(zhí)行流程

OkHttpClient
class Builder constructor() {
//Okhttp 請求分發(fā)器,是整個OkhttpClient的執(zhí)行核心
internal var dispatcher: Dispatcher = Dispatcher()
//Okhttp連接池,不過會把任務委托給RealConnectionPool處理
internal var connectionPool: ConnectionPool = ConnectionPool()
//用戶定義的攔截器,在重試攔截器之前執(zhí)行
internal val interceptors: MutableList<Interceptor> = mutableListOf()
//用戶定義的網(wǎng)絡攔截器,在CallServerInterceptor(執(zhí)行網(wǎng)絡請求攔截器)之前運行。
internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
//流程監(jiān)聽器
internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
//連接失敗時是否重連
internal var retryOnConnectionFailure = true
//服務器認證設置
internal var authenticator: Authenticator = Authenticator.NONE
//是否重定向
internal var followRedirects = true
//是否重定向到https
internal var followSslRedirects = true
//cookie持久化的設置
internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
//緩存設置
internal var cache: Cache? = null
//DNS設置
internal var dns: Dns = Dns.SYSTEM
//代理設置
internal var proxy: Proxy? = null
internal var proxySelector: ProxySelector? = null
internal var proxyAuthenticator: Authenticator = Authenticator.NONE
//默認的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的檢驗
internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
internal var certificateChainCleaner: CertificateChainCleaner? = null
//請求超時
internal var callTimeout = 0
//連接超時
internal var connectTimeout = 10_000
//讀取超時
internal var readTimeout = 10_000
//寫入超時
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)
在這里生成一個RealCall對象,這里第三個參數(shù)是否為websocket,默認是false。 在拿到RealCall對象之后,這里有兩種方式起發(fā)送網(wǎng)絡請求:
- execute() : 這種方式很少用
- enqueue() : 這種方式是將每個請求放在隊列中,按照順序逐個去進行消費。
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)
}
這里主要做了一下幾步
- 首先回調eventListener的callStart()方法,
- 然后把創(chuàng)建AsyncCall對象將responseCallback傳進去。
- 最后Dispatcher的enqueue()方法.
Dispatcher.enqueue()
class Dispatcher constructor() {
......
//按運行順序準備異步調用的隊列
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
//正在運行的異步請求隊列, 包含取消但是還未finish的AsyncCall
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
//正在運行的同步請求隊列, 包含取消但是還未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隊列中.
- 然后通過findExistingCallWithHost查找在runningAsyncCalls和readyAsyncCalls是否存在相同host的AsyncCall,如果存在則調用call.reuseCallsPerHostFrom()進行復用
- 最后調用 promoteAndExecute() 通過線程池執(zhí)行隊列中的AsyncCall對象
private fun promoteAndExecute(): Boolean {
this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>()
//判斷是否有請求正在執(zhí)行
val isRunning: Boolean
//加鎖,保證線程安全
synchronized(this) {
//遍歷 readyAsyncCalls 隊列
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隊列中移除并加入到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隊列,判斷runningAsyncCalls的數(shù)量是否大于最大并發(fā)請求數(shù)64, 判斷同一Host的請求是否大于5,然后將AsyncCall從readyAsyncCalls隊列中移除,并加入到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對象回調出去
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
//遇到IO異常 回調失敗方法
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)
}
}
}
}
這里可以看到AsyncCall就是一個Runable對象,線程執(zhí)行就會調用該對象的run方法,而executeOn方法就是執(zhí)行runable對象. 在run方法中主要執(zhí)行了以下幾步:
- 調用getResponseWithInterceptorChain()執(zhí)行OkHttp攔截器,獲取response對象
- 調用responseCallback的onResponse方法將Response對象回調出去
- 如果遇見IOException異常則調用responseCallback的onFailure方法將異?;卣{出去
- 如果遇到其他異常,調用cancel()方法取消請求,調用responseCallback的onFailure方法將異?;卣{出去
- 調用Dispatcher的finished方法結束執(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)
//構建攔截器責任鏈
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í)行攔截器責任鏈獲取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í)行了以下幾步操作
- 首先構建一個可變interceptor集合,將所有攔截器添加進去,這里如果是websocket則不添加networkInterceptor攔截器,這個interceptor集合的添加順序也就是OkHttp攔截器的執(zhí)行順序
- 構建一個RealInterceptorChain對象,將所有的攔截器包裹
- 調用RealInterceptorChain的proceed的方法,獲得Response對象
簡單的總結一下:這里才用了責任鏈設計模式,構建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)建了一個RealInterceptorChain()對象,不過需要注意的是index在創(chuàng)建對象時是index = index + 1,這樣就會執(zhí)行index對應下標的攔截器,不斷的調用下一個攔截器,直到有response對象返回,也就是chain.proceed(originalRequest)結束。
Interceptor
下面我們來具體分析一下攔截器

RetryAndFollowUpInterceptor
主要處理了如下幾個方向的問題:
- 1.異常,或者協(xié)議重試(408客戶端超時,權限問題,503服務暫時不處理,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) {
//這里會新建一個ExchangeFinder,ConnectInterceptor會使用到
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) {
//嘗試通過路由連接失敗。該請求將不會被發(fā)送。
if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
throw e.firstConnectException.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e.firstConnectException
}
newExchangeFinder = false
continue
} catch (e: IOException) {
//嘗試與服務器通信失敗。該請求可能已發(fā)送。
if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
throw e.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e
}
newExchangeFinder = false
continue
}
//嘗試關聯(lián)上一個response,注意:body是為null
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build()
}
val exchange = call.interceptorScopedExchange
//會根據(jù) responseCode 來判斷,構建一個新的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.調用RealCall的enterNetworkInterceptorExchange方法實例化一個
ExchangeFinder在RealCall對象中。 - 2.執(zhí)行RealCall的proceed 方法,進入下一個攔截器,進行下一步的請求處理。
- 3.如果出現(xiàn)路由異常,則通過recover方法校驗,當前的連接是否可以重試,不能重試則拋出異常,離開當前的循環(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
// 對于故障恢復,將相同的路由選擇器與新連接一起使用
return true
}
BridgeInterceptor
主要處理了如下幾個問題:
- 主要將Content-Type、Content-Length、Host等一些數(shù)據(jù)添加到頭部。
- 拿到數(shù)據(jù)之后對數(shù)據(jù)進行處理,判斷是否為gzip,進行對數(shù)據(jù)數(shù)據(jù)解壓。
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
//獲取原始請求數(shù)據(jù)
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")
}
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í)行下一個攔截器
val networkResponse = chain.proceed(requestBuilder.build())
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
//創(chuàng)建一個新的responseBuilder,目的是將原始請求數(shù)據(jù)構建到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()
}
- 設置頭部的Content-Type.說明內容類型是什么
- 如果contentLength大于等于0,則設置頭部的Content-Length(說明內容大小是多少);否則設置頭部的Transfer-Encoding為chunked(說明傳輸編碼為分塊傳輸)
- 如果Host不存在,設置頭部的Host(在Http 1.1之后出現(xiàn),可以通過同一個URL訪問到不同主機,從而實現(xiàn)服務器虛擬服務器的負載均衡。如果1.1之后不設置就會返回404)。
- 如果Connection不存在,設置頭部的Connection為Keep-Alive(代表連接狀態(tài)需要保持活躍)
- 如果Accept-Encoding且Range為空,則強制設置Accept-Encoding為gzip(說明請求將會以gzip方式壓縮)
- 從CookieJar的緩存中取出cookie設置到頭部的Cookie
- 如果User-Agent為空,則設置User-Agent到頭部
CacheInterceptor
用戶通過OkHttpClient.cache來配置緩存,緩存攔截器通過CacheStrategy來判斷是使用網(wǎng)絡還是緩存來構建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)絡,反之,則表示使用網(wǎng)絡
val networkRequest = strategy.networkRequest
//為空表示不使用緩存,反之,則表示使用緩存
val cacheResponse = strategy.cacheResponse
//追蹤網(wǎng)絡與緩存的使用情況
cache?.trackResponse(strategy)
val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE
//有緩存但不適用,關閉它
if (cacheCandidate != null && cacheResponse == null) {
cacheCandidate.body?.closeQuietly()
}
//如果網(wǎng)絡被禁止,但是緩存又是空的,構建一個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)絡不使用網(wǎng)絡,且有緩存,直接根據(jù)緩存內容構建并返回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í)行下一個攔截器
networkResponse = chain.proceed(networkRequest)
} finally {
//捕獲I/O或其他異常,請求失敗,networkResponse為空,且有緩存的時候,不暴露緩存內容
if (networkResponse == null && cacheCandidate != null) {
//否則關閉緩存響應體
cacheCandidate.body?.closeQuietly()
}
}
//如果有緩存
if (cacheResponse != null) {
//且網(wǎng)絡返回response code為304的時候,使用緩存內容新構建一個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 {
//否則關閉緩存響應體
cacheResponse.body?.closeQuietly()
}
}
//構建網(wǎng)絡請求的response
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
//如果cache不為null,即用戶在OkHttpClient中配置了緩存,則將上一步新構建的網(wǎng)絡請求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請求進行緩存,其它方法的請求則移除
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
//緩存無效,將該請求緩存從client緩存配置中移除
cache.remove(networkRequest)
} catch (_: IOException) {
}
}
}
return response
}
網(wǎng)絡請求前:
- 首先根據(jù)request從OkHttpClient.cache中獲取緩存,通過
CacheStrategy獲取本次請求的請求體及緩存的響應體。 - 如果 請求體
networkRequest和響應體cacheResponse都為空的話,則返回錯誤碼為 504 - 如果 請求體
networkRequest為空 響應體cacheResponse不為空的話,則將該響應體返回 - 如果請求體
networkRequest不為空的話,則進入下一個攔截器。
網(wǎng)絡請求后:
- 如果當前
cacheResponse不為空,且networkResponse狀態(tài)碼為304, 則代表數(shù)據(jù)沒有變化,那么就會根據(jù)cacheResponse構建一個新的response,根據(jù)當前時間更新到緩存當中,并返回到上一攔截器中 - 如果
networkResponse狀態(tài)碼不為304,則判斷是否進行緩存,最后返回到上一攔截器中
從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轉化為urf-8,并且通過md5拿到摘要,再調用hex獲取16進制的字符串,該字符串就是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ù)轉化為響應體
再來看看那些響應體需要緩存:
這里是網(wǎng)絡請求回來,判斷是否需要緩存的處理
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對象是否為空,決定是否進入緩存判斷
response.promisesBody()判斷響應體是否有正文,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 不進行緩存
以上就是Android開發(fā)OkHttp執(zhí)行流程源碼分析的詳細內容,更多關于Android OkHttp執(zhí)行流程的資料請關注腳本之家其它相關文章!
相關文章
Android利用ContentProvider獲取聯(lián)系人信息
這篇文章主要為大家詳細介紹了Android利用ContentProvider獲取聯(lián)系人信息,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11
Android UI實現(xiàn)廣告Banner輪播效果
這篇文章主要為大家詳細介紹了Android UI實現(xiàn)廣告Banner輪播效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12
SQLite數(shù)據(jù)庫在Android中的使用小結
SQLIte是一款輕型的數(shù)據(jù)庫,占用資源非常低,在嵌入式設備中,可能只需幾百k的內存,這篇文章主要介紹了SQLite數(shù)據(jù)庫在Android中的使用,需要的朋友可以參考下2024-07-07
Android Studio將AAR包發(fā)布到Maven本地倉庫的流程步驟
Android AAR文件是Android Archive文件的縮寫,是一種Android應用程序存檔文件格式,類似于JAR文件格式,它在Android Studio中被廣泛使用,本文給大家介紹了Android Studio將AAR包發(fā)布到Maven本地倉庫的流程步驟,需要的朋友可以參考下2025-03-03
詳解Android Activity之間跳轉出現(xiàn)短暫黑屏的處理方法
本篇文章主要介紹了詳解Android Activity之間跳轉出現(xiàn)短暫黑屏的處理方法,非常具有實用價值,需要的朋友可以參考下2017-06-06
studio碰到問題:java.lang.UnsatisfiedLinkError解決辦法
這篇文章主要介紹了studio碰到問題:java.lang.UnsatisfiedLinkError解決辦法的相關資料,需要的朋友可以參考下2017-02-02

