本篇基于 okhttp 4.9.3 進(jìn)行分析學(xué)習(xí)邀窃。
前言
Android 項(xiàng)目開發(fā)中離不開網(wǎng)絡(luò)請(qǐng)求,說到網(wǎng)絡(luò)請(qǐng)求一定會(huì)想到 okhttp。
okhttp 是如何完成一次請(qǐng)求的呢,接下來我們一步步來分析。
首先看下完整的流程圖,此處直接使用 Piasy 大佬的圖
一、如何發(fā)起請(qǐng)求
1. Android 工程中使用配置
添加網(wǎng)絡(luò)請(qǐng)求權(quán)限 <uses-permission android:name="android.permission.INTERNET"/>
添加依賴 implementation "com.squareup.okhttp3:okhttp:4.9.3"
2. 發(fā)起請(qǐng)求
以發(fā)送 GET
請(qǐng)求為例医咨,其他類型請(qǐng)求也是通過在創(chuàng)建 requst
對(duì)象時(shí)進(jìn)行設(shè)置。
// 創(chuàng)建 OkHttpClient 實(shí)例對(duì)象架诞,設(shè)置了超時(shí)時(shí)間
val client = OkHttpClient.Builder()
.callTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build()
// 創(chuàng)建 requset 對(duì)象拟淮,設(shè)置 url、請(qǐng)求方式侈贷、header 等信息
val request = Request.Builder().url(url).build()
// 同步執(zhí)行惩歉,需放到子線程中執(zhí)行
GlobalScope.launch(Dispatchers.IO) {
val response = client.newCall(request).execute().body?.string()
}
// 異步執(zhí)行
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.d(TAG, e.message ?: "have IOException")
}
override fun onResponse(call: Call, response: Response) {
val response = response.body?.string()
Log.d(TAG, "enqueue() response = $response")
}
})
二等脂、發(fā)起一次請(qǐng)求都經(jīng)歷了什么
分析上面 GET
請(qǐng)求時(shí)用到的對(duì)象,源碼只保留流程中重要的部分撑蚌。
1. OkHttpClient
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
...
// OkHttpClient 可以構(gòu)建 Call 的原因上遥,因?yàn)閷?shí)現(xiàn)了 Call.Factory
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
...
// 默認(rèn)構(gòu)建參數(shù)
class Builder constructor() {
internal var dispatcher: Dispatcher = Dispatcher() // 調(diào)度器
internal var connectionPool: ConnectionPool = ConnectionPool() // 連接池
internal val interceptors: MutableList<Interceptor> = mutableListOf() // 攔截器
internal val networkInterceptors: MutableList<Interceptor> = mutableListOf() // 網(wǎng)絡(luò)攔截器
internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
internal var retryOnConnectionFailure = true // 連接失敗時(shí)重試
internal var authenticator: Authenticator = Authenticator.NONE // 驗(yàn)證器
internal var followRedirects = true // 允許重定向
internal var followSslRedirects = true // 允許 https 同 http 之間的重定向
internal var cookieJar: CookieJar = CookieJar.NO_COOKIES // cookie
internal var cache: Cache? = null // 緩存
internal var dns: Dns = Dns.SYSTEM
internal var proxy: Proxy? = null
internal var proxySelector: ProxySelector? = null
internal var proxyAuthenticator: Authenticator = Authenticator.NONE
internal var socketFactory: SocketFactory = SocketFactory.getDefault()
internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
internal var x509TrustManagerOrNull: X509TrustManager? = null
internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
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
// 傳入自己構(gòu)建的參數(shù)
internal constructor(okHttpClient: OkHttpClient) : this() {
this.dispatcher = okHttpClient.dispatcher
this.connectionPool = okHttpClient.connectionPool
this.interceptors += okHttpClient.interceptors
this.networkInterceptors += okHttpClient.networkInterceptors
this.eventListenerFactory = okHttpClient.eventListenerFactory
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure
this.authenticator = okHttpClient.authenticator
this.followRedirects = okHttpClient.followRedirects
this.followSslRedirects = okHttpClient.followSslRedirects
this.cookieJar = okHttpClient.cookieJar
this.cache = okHttpClient.cache
this.dns = okHttpClient.dns
this.proxy = okHttpClient.proxy
this.proxySelector = okHttpClient.proxySelector
this.proxyAuthenticator = okHttpClient.proxyAuthenticator
this.socketFactory = okHttpClient.socketFactory
this.sslSocketFactoryOrNull = okHttpClient.sslSocketFactoryOrNull
this.x509TrustManagerOrNull = okHttpClient.x509TrustManager
this.connectionSpecs = okHttpClient.connectionSpecs
this.protocols = okHttpClient.protocols
this.hostnameVerifier = okHttpClient.hostnameVerifier
this.certificatePinner = okHttpClient.certificatePinner
this.certificateChainCleaner = okHttpClient.certificateChainCleaner
this.callTimeout = okHttpClient.callTimeoutMillis
this.connectTimeout = okHttpClient.connectTimeoutMillis
this.readTimeout = okHttpClient.readTimeoutMillis
this.writeTimeout = okHttpClient.writeTimeoutMillis
this.pingInterval = okHttpClient.pingIntervalMillis
this.minWebSocketMessageToCompress = okHttpClient.minWebSocketMessageToCompress
this.routeDatabase = okHttpClient.routeDatabase
}
...
}
使用建造者模式,用來設(shè)置攔截器争涌、超時(shí)時(shí)間粉楚、緩存、重定向等屬性亮垫。
2. Request
class Request internal constructor(
@get:JvmName("url") val url: HttpUrl,
@get:JvmName("method") val method: String,
@get:JvmName("headers") val headers: Headers,
@get:JvmName("body") val body: RequestBody?,
internal val tags: Map<Class<*>, Any>
) {
...
open class Builder {
internal var url: HttpUrl? = null
internal var method: String
internal var headers: Headers.Builder
internal var body: RequestBody? = null
internal var tags: MutableMap<Class<*>, Any> = mutableMapOf()
constructor() {
this.method = "GET"
this.headers = Headers.Builder()
}
internal constructor(request: Request) {
this.url = request.url
this.method = request.method
this.body = request.body
this.tags = if (request.tags.isEmpty()) {
mutableMapOf()
} else {
request.tags.toMutableMap()
}
this.headers = request.headers.newBuilder()
}
open fun url(url: HttpUrl): Builder = apply {
this.url = url
}
...
}
也是使用建造者模式模软,來設(shè)置請(qǐng)求方法、url饮潦、請(qǐng)求體燃异、header 信息。
3. Call
interface Call : Cloneable {
// 返回 call 的 request 對(duì)象
fun request(): Request
// 執(zhí)行同步請(qǐng)求
@Throws(IOException::class)
fun execute(): Response
// 執(zhí)行異步請(qǐng)求
fun enqueue(responseCallback: Callback)
// 取消
fun cancel()
// 是否已經(jīng)執(zhí)行請(qǐng)求
fun isExecuted(): Boolean
// 是否已經(jīng)取消
fun isCanceled(): Boolean
// 超時(shí)信息
fun timeout(): Timeout
// 克隆當(dāng)前 call继蜡,返回新的對(duì)象方便再次執(zhí)行請(qǐng)求回俐,同一個(gè) call 只能執(zhí)行一次
public override fun clone(): Call
fun interface Factory {
fun newCall(request: Request): Call
}
}
接口類核畴,定義了在一次請(qǐng)求中可能用到的方法寒亥。
4. RealCall
實(shí)現(xiàn) Call 接口,是最終的請(qǐng)求執(zhí)行者俺猿,下面按同步請(qǐng)求和異步請(qǐng)求分成兩部分來說碘举。
a忘瓦、同步請(qǐng)求部分
// RealCall.execute
override fun execute(): Response {
// 每個(gè) call 只能請(qǐng)求一次
check(executed.compareAndSet(false, true)) { "Already Executed" }
timeout.enter()
callStart()
try {
// 將當(dāng)前 call 添加到 Dispatcher 類的 runningSyncCalls 列表中
client.dispatcher.executed(this)
// 經(jīng)過一系列攔截器拿到請(qǐng)求結(jié)果
return getResponseWithInterceptorChain()
} finally {
// 將當(dāng)前 call 從 Dispatcher 類的 runningSyncCalls 列表中移除
client.dispatcher.finished(this)
}
}
// RealCall.getResponseWithInterceptorChain
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
// Build a full stack of interceptors.
val interceptors = mutableListOf<Interceptor>()
// 開始添加攔截器
// 在 OkHttpClient 中設(shè)置的 攔截器
interceptors += client.interceptors
// 處理重試和重定向的攔截器
interceptors += RetryAndFollowUpInterceptor(client)
// 將應(yīng)用請(qǐng)求轉(zhuǎn)換為網(wǎng)絡(luò)請(qǐng)求,將請(qǐng)求結(jié)果轉(zhuǎn)換為用戶友好響應(yīng)的攔截器
interceptors += BridgeInterceptor(client.cookieJar)
// 讀取緩存引颈、更新緩存的攔截器
interceptors += CacheInterceptor(client.cache)
// 和服務(wù)器連接的攔截器
interceptors += ConnectInterceptor
if (!forWebSocket) {
// 在 OkHttpClient 中設(shè)置的 networkInterceptors
interceptors += client.networkInterceptors
}
// 請(qǐng)求服務(wù)器耕皮、讀取響應(yīng)結(jié)果的攔截器
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ǔn)秸{(diào)用攔截器中的處理
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
// 返回結(jié)果
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
主要處理邏輯在 getResponseWithInterceptorChain()
該方法中,dispatcher
用來改變執(zhí)行狀態(tài)线欲。
b明场、異步請(qǐng)求部分
// RealCall.enqueue
override fun enqueue(responseCallback: Callback) {
// 每個(gè) call 只能請(qǐng)求一次
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
// 將 AsyncCall 添加到 Dispatcher.readyAsyncCalls 中
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
接下來看 enqueue()
方法
// Dispatcher.enqueue
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
// 添加到 readyAsyncCalls 中
readyAsyncCalls.add(call)
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
接下來看 promoteAndExecute()
方法
// Dispatcher.promoteAndExecute
private fun promoteAndExecute(): Boolean {
this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
val i = readyAsyncCalls.iterator()
// 遍歷 readyAsyncCalls
while (i.hasNext()) {
val asyncCall = i.next()
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
// 移除自己
i.remove()
asyncCall.callsPerHost.incrementAndGet()
// 添加到 executableCalls 中
executableCalls.add(asyncCall)
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
// 遍歷 executableCalls 開始執(zhí)行
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService)
}
return isRunning
}
執(zhí)行的 run 方法
// RealCall#AsyncCall.run
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
timeout.enter()
try {
// 同步請(qǐng)求時(shí)分析過的方法
val response = getResponseWithInterceptorChain()
signalledCallback = true
// 調(diào)用回調(diào)方法汽摹,拿到請(qǐng)求結(jié)果
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
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)
}
}
}
看到這里再去看開頭的流程圖就很容易了
分析了請(qǐng)求的大致流程李丰,細(xì)節(jié)分析后續(xù)有待完善。