首先先看一下用法:
val client = OkHttpClient.Builder().build()
val request = Request.Builder()
.url("https://www.baidu.com")
.build()
val call = client.newCall(request)
call.enqueue(object : okhttp3.Callback {
override fun onFailure(call: okhttp3.Call, e: IOException) {
}
override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
Log.e("TAG", "onResponse: ${response.body?.string()}")
}
})
首先通過 builder 模式創建了一個 client。然後通過 newCall 方法創建了一個 Call ,最後通過這個 call 進行網絡請求。
newCall 方法中傳了一個參數,是 Request,這個 Request 是自己拼出來的。這個方法就是通過 request 創建一個 待用的網絡請求。
override fun newCall(request: Request): Call {
return RealCall.newRealCall(this, request, forWebSocket = false)
}
// RealCall
companion object {
fun newRealCall(
client: OkHttpClient, originalRequest: Request, forWebSocket: Boolean
): RealCall {
//創建一個 RealCall
//client ,requet,webSocket一般情況是用不到
return RealCall(client, originalRequest, forWebSocket).apply {
//Transmitter
transmitter = Transmitter(client, this)
}
}
}
在 newRealCall 方法中返回了一個 RealCall 對象,由此可知,RealCall 是 Call 的實現類。
enqueue 方法
call.enqueue(object : okhttp3.Callback {
override fun onFailure(call: okhttp3.Call, e: IOException) {
}
override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
Log.e("TAG", "onResponse: ${response.body?.string()}")
}
})
//RealCall
override fun enqueue(responseCallback: Callback) {
synchronized(this) {
check(!executed) { "Already Executed" }
executed = true
}
transmitter.callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
在 enqueue 中 將 callback 轉交給了 dispather,並創建了一個異步的Call 。下面分別看一下 dispatcher,enqueue 和 AsyncCall 分別是什麼東西
-
dispatcher
val dispatcher: Dispatcher = builder.dispatcher //異步請求何時執行的策略。每個調度程序使用一個[ExecutorService]在內部運行調用。 //如果您提供自己的執行器,它應該能夠同時運行[配置的最大][maxRequests]調用數。 class Dispatcher constructor() { // 最大請求數,超過後就會等一等 @get:Synchronized var maxRequests = 64 //...... //主機的最大請求數,防止給服務器太大壓力 @get:Synchronized var maxRequestsPerHost = 5 @get:Synchronized @get:JvmName("executorService") val executorService: ExecutorService get() { if (executorServiceOrNull == null) { executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS, SynchronousQueue(), threadFactory("OkHttp Dispatcher", false)) } return executorServiceOrNull!! } }
Dispatcher 主要是用來管理線程的,每一個新的請求的過程是需要一個單獨的線程,這樣不同的請求之間不會被擋着。
他的內部實現用的是 ExecutorService ,
-
dispatcher 的 enqueue
internal fun enqueue(call: AsyncCall) { synchronized(this) { readyAsyncCalls.add(call) //..... } promoteAndExecute() }
調用 readyAsyncCalls ,這個是一個待命的隊列,隨時準備執行。
private fun promoteAndExecute(): Boolean { assert(!Thread.holdsLock(this)) val executableCalls = mutableListOf<AsyncCall>() val isRunning: Boolean synchronized(this) { val i = readyAsyncCalls.iterator() // 遍歷待命隊列 while (i.hasNext()) { val asyncCall = i.next() //判斷 最大連接數 和 host 數量,不滿足直接 break if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity. if (asyncCall.callsPerHost().get() >= this.maxRequestsPerHost) continue // Host max capacity. // 從待命中移除 i.remove() asyncCall.callsPerHost().incrementAndGet() //將 asynCall 添加到 list 和 運行隊列中 executableCalls.add(asyncCall) runningAsyncCalls.add(asyncCall) } isRunning = runningCallsCount() > 0 } for (i in 0 until executableCalls.size) { val asyncCall = executableCalls[i] // 調用 AsyncCall 的 executeOn 開始執行 asyncCall.executeOn(executorService) } return isRunning }
dispatcher 的 enqueue 主要就是進行了最大連接數和host的判斷,然後將 asyncCall 添加到 一個 list 和 運行隊列中,最後遍歷 list 調用了 AsyncCall 的 executeOn 方法,
下面看一下 AsyncCall
-
AsyncCall
internal inner class AsyncCall( private val responseCallback: Callback) : Runnable { fun executeOn(executorService: ExecutorService) { assert(!Thread.holdsLock(client.dispatcher)) var success = false try { //執行 executorService.execute(this) success = true } catch (e: RejectedExecutionException) { val ioException = InterruptedIOException("executor rejected") ioException.initCause(e) transmitter.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 transmitter.timeoutEnter() 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(INFO, "Callback failure for ${toLoggableString()}", e) } else { // responseCallback.onFailure(this@RealCall, e) } } finally { client.dispatcher.finished(this) } } } } }
注意 executeOn 方法,這個方法就是 在 dispatcher 的 enqueue 中調用的。在這裏執行了 executorService 後,對應的 run 方法也就會執行。
run 方法中,通過 getResponseWithInterceptorChain() 進行請求,並拿到響應 response,最後調用 callback,這個 callback 就是我們在請求的時候傳入的 callback
在 run 中有一些比較重要的方法,我們到後面說。
到現在使用 enqueue 進行請求的大致邏輯已經非常清楚了,其中最關鍵的代碼就是:
client.dispatcher.enqueue(AsyncCall(responseCallback))
大致流程如下:
1,創建一個 RealCall ,然後調用 enqueue
2,在 enqueue 中調用 dispatcher 的 enqueue
3,在 dispatcher 的 enqueue 方法中觸發了 AsyncCall 的 run 方法,
4,在 run 方法中進行請求並響應
execute 方法
val response = call.execute()
override fun execute(): Response {
synchronized(this) {
check(!executed) { "Already Executed" }
executed = true
}
transmitter.timeoutEnter()
transmitter.callStart()
try {
client.dispatcher.executed(this)
// 非常直接,直接調用了。
return getResponseWithInterceptorChain()
} finally {
client.dispatcher.finished(this)
}
}
execute 中不需要切線程,所以就直接調用了。
OkhttpClient 中的配置項
-
dispatcher
用來調度線程的,對性能進行優化等,例如 maxRequest = 64 等
-
proxy
代理
-
protocols
所支持 http 協議的版本
enum class Protocol(private val protocol: String) { HTTP_1_0("http/1.0"), HTTP_1_1("http/1.1"), HTTP_2("h2"), //...... }
-
connectionSpecs
ConnectionSpecs 是用來指定 http 傳輸時的 socket 連接;對於 https 連接,ConnectionSpecs 是在構建 TLS 連接時向服務端說明客戶端支持的 TLS 版本,密碼套件的一類,
在通過 https 連接時,需要向服務器附加上 客戶端所支持的 TLS 版本,Cipher suite(可以接受的 對稱,非對稱和 hash 算法) 等數據。這些都在 ConnectionSpecs 這個類中。
// 支持密碼套件的列表 private val RESTRICTED_CIPHER_SUITES = arrayOf( // TLSv1.3. CipherSuite.TLS_AES_128_GCM_SHA256, CipherSuite.TLS_AES_256_GCM_SHA384, CipherSuite.TLS_CHACHA20_POLY1305_SHA256, // TLSv1.0, TLSv1.1, TLSv1.2. CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) //支持密碼套件的列表 private val APPROVED_CIPHER_SUITES = arrayOf( // TLSv1.3. CipherSuite.TLS_AES_128_GCM_SHA256, CipherSuite.TLS_AES_256_GCM_SHA384, CipherSuite.TLS_CHACHA20_POLY1305_SHA256, // TLSv1.0, TLSv1.1, TLSv1.2. CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, // Note that the following cipher suites are all on HTTP/2's bad cipher suites list. We'll // continue to include them until better suites are commonly available. CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA) /** 安全的TLS連接需要最近的客戶端和最近的服務器 */ @JvmField val RESTRICTED_TLS = Builder(true) .cipherSuites(*RESTRICTED_CIPHER_SUITES) .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2) .supportsTlsExtensions(true) .build() /** *現代的TLS的配置,適用於大多數客戶端和可以連接到的服務器,是OkHttp的默認配置 */ @JvmField val MODERN_TLS = Builder(true) .cipherSuites(*APPROVED_CIPHER_SUITES) .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2) .supportsTlsExtensions(true) .build() /** *向後兼容的配置,相比於MODERN_TLS,支持的TLS的版本變少了,只支持TLS_1_0版本 */ @JvmField val COMPATIBLE_TLS = Builder(true) .cipherSuites(*APPROVED_CIPHER_SUITES) .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0) .supportsTlsExtensions(true) .build() /** 未加密未認證的連接,就是HTTP連接*/ @JvmField val CLEARTEXT = Builder(false).build()
上面的 xxxx_TLS 配置的就是你要使用 http 還是使用 https 。
如果你要使用 https ,那麼你是用的 tls 版本是多少
-
interceptors,networkInterceptors
攔截器
-
eventListenerFactory
用來做統計的。
-
cookieJar
cookie 的存儲器。okhttp 默認沒有實現 cookie 的存儲,需要自己去存儲。
-
cache
緩存
-
socketFactory
用來創建 TCP 端口的 ,http 本身是沒有端口的。
-
certificateChainCleaner
從服務器拿下來的證書,有時候會拿到很多個證書,certificateChainCleaner 是用來整理的,整理完後就是一個鏈或者說是一個序列,最後一個證書就是本地的根證書。
-
hostnameVerifier
給 https 做主機名驗證的,用來驗證對方的 host 是不是你需要訪問的host
-
certificatePinner
證書固定器,用來驗證自簽名證書的!
-
connectionPool
連接池
-
followRedirects
遇到 301 的時候是否需要重定向
-
followSslRedirects
當你訪問的是 http ,但是重定向到 https,或者是 訪問的是 https ,但是重定向到 http 的時候 是否要重定向。
-
retryOnConnectionFailure
請求失敗的時候是否需要重連
-
connectTimeout
tcp 連接的時間,超過這個事件則報錯
-
readTimeout
下載響應的時候等待時間
-
writeTimeout
寫入請求的等待時間
-
pingInterval
針對 webSocket 的,可以雙向交互的通道,這就需要長連接了。pingInterval 表示多少時間 ping 一次。
getResponseWithInterceptorChain
在 enqueue 和 execute 方法中,最後都調用的是 getResponseWithInterceptorChain 方法進行請求,在這個方法中會將你的請求進行網絡請求,並獲得 response。下面看一哈源碼
fun getResponseWithInterceptorChain(): Response {
// 獲取 和 創建所有的攔截器
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)
//.....
val response = chain.proceed(originalRequest)
//......
}
上面首先是獲取了自己添加的攔截器,然後又添加了許多內置的攔截器。
接着就是創建了一個 chain ,這個 chain 就是一個鏈,這個鏈裏面放的的就是攔截器。
在請求的時候,就會從鏈的開始往後執行,請求完成之後又會返回來。
chain 的作用:例子,我是店老闆,收到了一份快餐的訂單,然後我將快餐做好之後交給店裏騎手,接着騎手把快餐送到客戶家,客戶收到後將錢交給騎手,騎手會從錢中拿出一部分當做路飯,剩下的就給老闆。
這就是一個鏈,老闆是起始端,負責製作快餐,並交給騎手;中間的節點就是騎手,負責將快餐送給用戶;終點就是客戶,接收快餐後,進行付錢。
鏈中的每一個節點都會對 request 做一些處理,並轉交給下一個節點,一直到最後,並且會返回到最開始的地方。
而網絡請求的時候會創建 chain,其中的節點就是攔截器,他會對 request 做一些或多或少的處理,然後交給下一個節點處理。
override fun proceed(request: Request): Response {
return proceed(request, transmitter, exchange)
}
@Throws(IOException::class)
fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
if (index >= interceptors.size) throw AssertionError()
calls++
//.....
// 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")
//......
return response
}
在 proced 中,獲取下一個攔截器,並調用他的 intercept 方法,在 intercept 中,又會調用 proced,獲取下一個攔截器。。。。;這裏就會形成一個循環,直到最後一個節點返回 response ,最終數據就會返回的開始調用的地方。
需要注意的是,在調用下一個攔截器的時候,當前的 intercept 是沒有執行完的。
接着看一下一些內置的攔截器
// 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)
-
RetryAndFollowUpInterceptor
重試,並在必要時跟隨重定向。它可能會拋出IOException
-
BridgeInterceptor(client.cookieJar)
連接應用程序代碼和網絡代碼。首先,它根據用戶請求構建網絡請求。然後它繼續調用網絡。最後,它從網絡響應構建一個用戶響應。
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") } var transparentGzip = false if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) { transparentGzip = true //支持 gzip 數據類型 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) } // proced 之前,請求前 val networkResponse = chain.proceed(requestBuilder.build()) // proced 之後,請求後 cookieJar.receiveHeaders(userRequest.url, networkResponse.headers) val responseBuilder = networkResponse.newBuilder() .request(userRequest) //解 gzip 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() }
在 proced 之前,設置一寫通用的設置,這些設置是在 header 中的,如 contentType,長度,host 等一系列東西。
在 proced 之後如果服務器發送的是 gzip 數據,則進行解壓,將數據添加到 body 中,最後返回 response
-
CacheInterceptor
做一些緩存的處理,如 請求之前判斷是否有緩存,請求成功後緩存的寫入等
-
ConnectInterceptor
處理 http ,https ,tcp 連接的問題,和網絡進行交互,並返回 response
override fun intercept(chain: Interceptor.Chain): Response { val realChain = chain as RealInterceptorChain val request = realChain.request() val transmitter = realChain.transmitter() // We need the network to satisfy this request. Possibly for validating a conditional GET. val doExtensiveHealthChecks = request.method != "GET" val exchange = transmitter.newExchange(chain, doExtensiveHealthChecks) return realChain.proceed(request, transmitter, exchange) }
-
networkInterceptors
NetWorkIntercept 是放在最後面的,這種攔截器是用來做網絡相關的操作的。
如有問題,還請指出