什麼是OkHttp?
OkHttp在Android開發領域裏面應該是無人不知了吧。它是一個由Square公司開源的第三方庫。主要用於處理網絡請求。
OkHttp Github地址:https://github.com/square/okhttp/
關於Square公司,大家可以去看看他們的其他開源庫,質量都挺高的,不僅包括Android相關的,還有Go,Ruby,JS,Kotlin等等
Square官方網站:https://square.github.io/
到目前爲止,OkHttp的最新版本是4.3.1,裏面一部分代碼也已經用Kotlin來重寫了,而大部分分析OkHttp源碼的文章還停留在以前舊的版本,因此在寫這系列源碼分析的文章同時,也是學習Kotlin的一個好機會。
從一個簡單的請求說起
如果有做過Android開發的話,相信以下代碼大家都很熟悉了:
val client = OkHttpClient()
val request = Request.Builder().url("https://www.baidu.com").build()
val response = client.newCall(request).enqueue(object: Callback{
override fun onFailure(call: Call, e: IOException) {
println("bluelzy --- ${e.message}")
}
override fun onResponse(call: Call, response: Response) {
println("bluelzy --- ${response.body.toString()}")
}
})
這段代碼做的事情很簡單:
-
創建一個OkHttpClient對象
-
使用建造者模式創建一個Request對象
-
使用OkHttpClient.newCall(request).enqueue() 發起請求,並處理回調
但是這短短的幾行代碼裏面,就已經有很多我們可以學習的東西了。
首先是建造者模式,也叫Builder模式,它的作用是讓用戶自由組合需要的參數,來實現不同的需求。具體實現方法可以通過接口,也可以像OkHttp一樣,使用內部類。這裏我們不詳細展開闡述,有興趣的讀者可以去看看《大話設計模式》或者《Android源碼設計模式分析與實戰》。
然後,通過val
這種寫法來定義變量也不太優雅,因爲這些變量其實聲明一次就夠了,我們可以改進一下代碼:
// 定義Request
Request.Builder().url("https://www.baidu.com").build().let { request ->
// 定義OkHttpClient
OkHttpClient().newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
println("bluelzy --- ${e.message}")
}
override fun onResponse(call: Call, response: Response) {
println("bluelzy --- ${response.body.toString()}")
}
})
}
把聲明的三個變量都幹掉,這樣代碼看起來是不是簡潔多了?
上面就是一個最簡單的OkHttp發起GET請求的方式,下面我們一起來看看OkHttp是怎麼做到的。
1.創建OkHttpClient
這個類的作用就是發起HTTP請求和接收響應
首先OkHttpClient有兩個構造方法,一個是無參構造方法,另外一個是傳入參數爲Builder的構造方法
OkHttpClient的構造方法
無參構造方法
constructor() : this(Builder())
有參構造方法
open class OkHttpClient internal constructor( builder: Builder )
OkHttpClient.Builder類
可以看到,無參構造方法其實也是調用了Builder爲參數的構造方法,這裏傳入的Builder() 就是默認的實現,繼續看看Builder裏面是怎麼實現的:
class Builder constructor() {
internal var dispatcher: Dispatcher = Dispatcher() // 調度器
internal var connectionPool: ConnectionPool = ConnectionPool() // 連接池
internal val interceptors: MutableList<Interceptor> = mutableListOf()//攔截器
// 網絡攔截器
internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
// 事件監聽
internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
internal var retryOnConnectionFailure = true
internal var authenticator: Authenticator = Authenticator.NONE
internal var followRedirects = true
internal var followSslRedirects = true
internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
// 緩存
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
// Socket工廠
internal var socketFactory: SocketFactory = SocketFactory.getDefault()
// SSL Socket工廠,用於HTTPS
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
// 驗證確認響應鏈,用於HTTPS
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 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
}
Builder是OkHttpClient的內部類,然後Builder聲明瞭很多的參數,這些參數的作用我會在後面的文章中詳細分析。
到這裏爲止,我們就完成了第一步,創建一個OkHttpClient對象,在構造方法裏面創建了Builder()對象。然後看第二步:Request.Builder() 創建Request對象
2.創建Request對象
Request的構造方法
Request的構造方法需要4個參數
class Request internal constructor(
@get:JvmName("url") val url: HttpUrl, // 請求的url
@get:JvmName("method") val method: String, // 請求方法類型
@get:JvmName("headers") val headers: Headers, // 請求頭
@get:JvmName("body") val body: RequestBody?, // 請求體
internal val tags: Map<Class<*>, Any>
)
Request.Builder類
Request同樣使用了Builder模式,我們直接看Request.Builder類:
open class Builder {
internal var url: HttpUrl? = null
internal var method: String
internal var headers: Headers.Builder
internal var body: RequestBody? = null
/** A mutable map of tags, or an immutable empty map if we don't have any. */
internal var tags: MutableMap<Class<*>, Any> = mutableMapOf()
// 無參構造方法
constructor() {
// 默認爲GET
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
}
// 省略代碼
}
可以看到,Builder默認的請求方法是GET,並且初始化了一個大小爲20的ArrayList作爲Headers的容器
除了method和haders之外,還有兩個關鍵的參數,一個是url,另外一個就是RequestBody。這幾個參數都提供了set方法,支持鏈式調用,例如url:
open fun url(url: String): Builder {
// Silently replace web socket URLs with HTTP URLs.
val finalUrl: String = when {
url.startsWith("ws:", ignoreCase = true) -> {
"http:${url.substring(3)}"
}
url.startsWith("wss:", ignoreCase = true) -> {
"https:${url.substring(4)}"
}
else -> url
}
return url(finalUrl.toHttpUrl())
}
這裏做了一點處理,如果是基於web socket協議的url,會被替換成http,而wws則是加密的web socket協議。
設置完了url / requestBody / mothod / headers 之後,我們都會調用build()方法:
open fun build(): Request {
return Request(
checkNotNull(url) { "url == null" },
method,
headers.build(),
body,
tags.toImmutableMap()
)
}
其實就是把剛剛設置的作爲參數,調用Request的有參構造方法,創建一個Request對象。到這裏爲止,第二步也就完成了。
3.發起請求
做完前面兩步之後,終於到了激動人心的時刻了,我們通過OkHttpClient.newCall(request).enqueue()來發起請求
到源碼裏面看看newCall和enqueue這兩個方法分別做了什麼
newCall()方法
override fun newCall(request: Request): Call {
return RealCall.newRealCall(this, request, forWebSocket = false)
}
調用RealCall.newRealCall方法,並且傳入了OkHttpClient和Request作爲參數
再看看newRealCall:
companion object {
fun newRealCall(
client: OkHttpClient,
originalRequest: Request,
forWebSocket: Boolean
): RealCall {
// Safely publish the Call instance to the EventListener.
return RealCall(client, originalRequest, forWebSocket).apply {
transmitter = Transmitter(client, this) // okhttp和網絡層的中介
}
}
}
其實就是創建了一個RealCall對象,注意這裏同時創建了一個Transmitter對象。它後面會再出現的,暫時先忽略,我們先回到發起請求這個過程中來。
enqueue()方法
上一步創建了RealCall對象是吧,傳入了OkHttpClient和Request對象是吧,那麼enqueue()方法又做了什麼呢?
這個enqueue()方法其實是Call接口的其中一個方法,除了enqueue之外,還有request(), execute()等其他方法。我們先看這個enqueue()方法:
override fun enqueue(responseCallback: Callback) {
synchronized(this) {
check(!executed) { "Already Executed" }
executed = true
}
transmitter.callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
override fun cancel() {
transmitter.cancel()
}
可以看到,首先這個方法有一個Callback作爲參數,而這個Callback接口裏面又有兩個方法:
interface Callback {
/**
* Called when the request could not be executed due to cancellation, a connectivity problem or
* timeout. Because networks can fail during an exchange, it is possible that the remote server
* accepted the request before the failure.
*/
fun onFailure(call: Call, e: IOException)
/**
* Called when the HTTP response was successfully returned by the remote server. The callback may
* proceed to read the response body with [Response.body]. The response is still live until its
* response body is [closed][ResponseBody]. The recipient of the callback may consume the response
* body on another thread.
*
* Note that transport-layer success (receiving a HTTP response code, headers and body) does not
* necessarily indicate application-layer success: `response` may still indicate an unhappy HTTP
* response code like 404 or 500.
*/
@Throws(IOException::class)
fun onResponse(call: Call, response: Response)
}
分別用於處理失敗和成功兩種情況的回調。
在enqueue的方法體內,首先通過一個synchronized關鍵字,確保這個方法只會執行一次,
然後通過dispatcher.enqueue來執行異步請求
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.get().forWebSocket) {
val existingCall = findExistingCallWithHost(call.host())
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
繼續看看promoteAndExecute()
private fun promoteAndExecute(): Boolean {
this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
val i = readyAsyncCalls.iterator()
while (i.hasNext()) {
val asyncCall = i.next()
if (runningAsyncCalls.size >= this.maxRequests) break // 最大請求數爲64
if (asyncCall.callsPerHost().get() >= this.maxRequestsPerHost) continue // 相同的Host最大同時請求數爲5
i.remove()
asyncCall.callsPerHost().incrementAndGet()
executableCalls.add(asyncCall) // 把請求加入到list中
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService) //
}
return isRunning
}
首先判斷是不是超過了最大請求數或者是相同Host的最大請求數,如果是的話就直接return
否則就執行asyncCall.excuteOn方法
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
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!
}
}
}
AsyncCall是RealCall裏面的一個內部類,因此在這裏已經持有了OkHttpClient對象,也就持有了Dispatcher,而這個executorService又是什麼呢?
其實這是一個線程池執行器,在Dispatcher中定義爲一個變量
@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!!
}
而 executorService.execute(this) 中的 this
指的就是AsyncCall對象,一個實現了Runnable接口的對象
因此,上面的excuteOn方法,其實就是執行AsyncCall的run()方法啊。
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
transmitter.timeoutEnter()
try {
val response = getResponseWithInterceptorChain() // 1
signalledCallback = true
responseCallback.onResponse(this@RealCall, response) //2
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log("Callback failure for ${toLoggableString()}", 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)
}
}
}
這裏又用到了Kotlin的內聯函數,相當於在方法體外面多加了一層try…finally,關於內聯函數,大家可以看看官方文檔的說明:Kotlin中文網 - 內聯函數
inline fun threadName(name: String, block: () -> Unit) {
val currentThread = Thread.currentThread()
val oldName = currentThread.name
currentThread.name = name
try {
block()
} finally {
currentThread.name = oldName
}
}
首先我們來看註釋1
@Throws(IOException::class)
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(interceptors, transmitter, null, 0, originalRequest, this,
client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis)
var calledNoMoreExchanges = false
try {
val response = chain.proceed(originalRequest)
if (transmitter.isCanceled) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw transmitter.noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null)
}
}
}
這裏用到了另外一個設計模式:責任鏈模式,用來處理整個網絡請求中不同的部分,例如失敗重試,緩存,連接等等。這些不同的部分都通過攔截器的方式來實現。
在chain.proceed(originalRequest)
方法中,其實就是調用了RealInterceptorChain.proceed()方法
這個方法的源碼:
@Throws(IOException::class)
fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
if (index >= interceptors.size) throw AssertionError()
calls++
// If we already have a stream, confirm that the incoming request will use it.
check(this.exchange == null || this.exchange.connection()!!.supportsUrl(request.url)) {
"network interceptor ${interceptors[index - 1]} must retain the same host and port"
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
check(this.exchange == null || calls <= 1) {
"network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
}
// 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")
// Confirm that the next interceptor made its required call to chain.proceed().
check(exchange == null || 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
}
可以看到,通過index + 1的方法,我們取出下一個攔截器,然後執行裏面的intercept方法,這就是proceed方法主要進行的工作。最終把Response返回。得到的這個Response又通過Callback.onResponse回調方法,使得我們可以獲取到這個Response對象。而如果中途拋出異常,那麼則會回調onFailure方法。
至於我們在上面加入的那些攔截器,詳細的說明會放到下一篇文章中講解,我們也會加入自定義攔截器的例子。畢竟這種情況在實際開發中還是經常會遇到的,例如自定義ConverterFactory來解析後臺返回的數據。
總結
最後,讓我們再來回顧一下OkHttp是如何發起請求的:
- 構造一個OkHttpClient對象,這裏有許多的變量用於控制請求時候的參數
- 構造一個Request對象,這裏主要是4個要素:url, method, header, body
- 調用OkHttpClient.newCall(request).enqueue()方法發起請求,在RealCall類中,實現了Call接口,並且持有OkHttpClient和Request對象,還有一個AsyncCall的內部類,這個內部類就是用來發起異步請求的,這個類同時也實現了Runnable接口,它的getResponseWithInterceptorChain()方法通過責任鏈的模式,把請求相關的攔截器一個個加入到List中,然後再通過RealInterceptorChain的proceed()方法來執行這些不同的攔截器所定義的方法。
- 最後成功則回調Callback.onResponse, 失敗回調Callback.onFailure
還有,到目前爲止我們已經發現了OkHttp使用了兩個設計模式,分別是:
- Builder模式
- 責任鏈模式
有興趣的童鞋可以自行上網查找相關資料。
好了,一個簡單的請求大致上就是這麼個流程,下一篇文章我們繼續深入瞭解OkHttp裏面的攔截器~