OkHttp概述
OkHttp 是適用於 Android 和 Java 應用程序的高效HTTP客戶端。最早的版本是基於 HttpURLConnection 和 HttpClient 的封裝,之後 Android 6.0 版移除了對 Apache HTTP 客戶端的支持,OkHttp 也移除了 HttpClient,然後移除 HttpURLConnection 的底層支持,連接底層到應用全部自己實現,成爲業界公認最佳方案,HttpURLConnection 的底層實現也改爲了 OkHttp 的實現方式。
優勢:
支持HTTP/2, HTTP/2通過使用多路複用技術在一個單獨的TCP連接上支持併發, 通過在一個連接上一次性發送多個請求來發送或接收數據
如果HTTP/2不可用, 連接池複用技術也可以極大減少延時
支持GZIP, 可以壓縮下載體積
響應緩存可以直接避免重複請求
會從很多常用的連接問題中自動恢復
如果您的服務器配置了多個IP地址, 當第一個IP連接失敗的時候, OkHttp會自動嘗試下一個IP
OkHttp還處理了代理服務器問題和SSL握手失敗問題
使用
使用 OkHttp 同步請求
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(BASE_URL + "/date")
.build();
Call call = client.newCall(request);
Response response = call.execute();
使用 OkHttp 異步請求
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(BASE_URL + "/date")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
public void onResponse(Call call, Response response)
throws IOException {
// ...
}
public void onFailure(Call call, IOException e) {
fail();
}
});
源碼走讀
異步請求
從 call.enqueue
開始,查看方法實現:
fun enqueue(responseCallback: Callback)
這是一個抽象方法,所以需要回到Call call = client.newCall(request)
,查看newCall
的實現,
/** Prepares the [request] to be executed at some point in the future. */
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
那麼真正調用enqueue
的是RealCall
,查看 RealCall
裏enqueue
的實現:
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
callStart()
是一個監聽器,監聽請求的開始,重點在這裏:client.dispatcher.enqueue(AsyncCall(responseCallback))
。點進dispatcher
:
/**
* Policy on when async requests are executed.
*
* Each dispatcher uses an [ExecutorService] to run calls internally. If you supply your own
* executor, it should be able to run [the configured maximum][maxRequests] number of calls
* concurrently.
*/
class Dispatcher constructor() {}
通過註釋,可以知道這是一個使用 ExecutorService 的線程調度器。
/**
* The maximum number of requests to execute concurrently. Above this requests queue in memory,
* waiting for the running calls to complete.
*
* If more than [maxRequests] requests are in flight when this is invoked, those requests will
* remain in flight.
*/
@get:Synchronized var maxRequests = 64
set(maxRequests) {
require(maxRequests >= 1) { "max < 1: $maxRequests" }
synchronized(this) {
field = maxRequests
}
promoteAndExecute()
}
可以 Set 的同時執行的最大請求數,默認同時執行64個請求。
/**
* The maximum number of requests for each host to execute concurrently. This limits requests by
* the URL's host name. Note that concurrent requests to a single IP address may still exceed this
* limit: multiple hostnames may share an IP address or be routed through the same HTTP proxy.
*
* If more than [maxRequestsPerHost] requests are in flight when this is invoked, those requests
* will remain in flight.
*
* WebSocket connections to hosts **do not** count against this limit.
*/
@get:Synchronized var maxRequestsPerHost = 5
set(maxRequestsPerHost) {
require(maxRequestsPerHost >= 1) { "max < 1: $maxRequestsPerHost" }
synchronized(this) {
field = maxRequestsPerHost
}
promoteAndExecute()
}
每個主機能同時執行的最大請求數,默認是5個。
@get:Synchronized
@get:JvmName("executorService") val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
}
return executorServiceOrNull!!
}
內部使用一個核心線程數爲0、容量無限大、60秒超時的線程池。
查看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.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
readyAsyncCalls.add(call)
把準備執行但未執行的 AsyncCall 放入隊列,然後記錄主機連接數,最後調用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 // Max capacity.
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
i.remove()
asyncCall.callsPerHost.incrementAndGet()
executableCalls.add(asyncCall)
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService)
}
return isRunning
}
遍歷隊列,挑選符合條件可以被執行的 Call ,把他們放在正在執行的隊列,然後調用asyncCall.executeOn(executorService)
執行:
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)
noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
executorService.execute(this)
這裏既然傳入了this
,那麼這個類肯定實現了 Runnable 接口:
inner class AsyncCall(
private val responseCallback: Callback
) : Runnable
我們查看 AsyncCall 的 run
方法,找出怎麼執行的:
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
timeout.enter()
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("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)
}
}
}
}
val response = getResponseWithInterceptorChain()
這裏直接返回的是 Response,說明getResponseWithInterceptorChain
裏會發起請求和返回,核心也就在這裏。
同步請求
下面看下同步請求的執行:
override fun execute(): Response {
check(executed.compareAndSet(false, true)) { "Already Executed" }
timeout.enter()
callStart()
try {
client.dispatcher.executed(this)
return getResponseWithInterceptorChain()
} finally {
client.dispatcher.finished(this)
}
}
很簡單,直接調用getResponseWithInterceptorChain()
。
整體架構分析到此,下一篇詳細分析getResponseWithInterceptorChain()
。