今天看了okhttp的源碼,做一下記錄悯蝉。比較low鼻由,也是無(wú)數(shù)人寫(xiě)過(guò)的內(nèi)容了蕉世,但是自己看過(guò)總要留下點(diǎn)筆記狠轻。
一個(gè)最簡(jiǎn)單的網(wǎng)絡(luò)請(qǐng)求框架應(yīng)該具有兩個(gè)基本要素:執(zhí)行網(wǎng)絡(luò)請(qǐng)求的類(lèi)(httpurlconnection那種)和執(zhí)行任務(wù)的線程池哈误。
那么自然就引出了網(wǎng)絡(luò)請(qǐng)求任務(wù)的管理策略問(wèn)題躏嚎,okhttp是搞了一個(gè)Dispatcher來(lái)做任務(wù)管理的類(lèi)菩貌。
Dispatcher中有兩個(gè)ArrayDeque的隊(duì)列箭阶,runningAsyncCalls(正在執(zhí)行的網(wǎng)絡(luò)請(qǐng)求)和readyAsyncCalls(將要執(zhí)行的等待隊(duì)列)來(lái)管理異步請(qǐng)求的隊(duì)列仇参。(注:ArrayDeque內(nèi)部包含了一個(gè)數(shù)組和兩個(gè)代表頭尾的指針诈乒,是線程不安全的,因此效率高于LinkedList消约。此外或粮,在源碼中看到氯材,ArrayDeque還可以在iterator遍歷時(shí)指定移除某一個(gè)元素氢哮。厲害了我的ArrayDeque)
在我們寫(xiě)call.enqueue時(shí)命浴,調(diào)用的是RealCall的enqueue方法生闲,
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
可以看到是調(diào)用了client.dispatcher().enqueue方法將AsyncCall包裹的callback加入了隊(duì)列。進(jìn)入dispatcher類(lèi)可以看到:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
可以看到捉兴,運(yùn)行中的隊(duì)列最大容量為maxRequests (默認(rèn)值64)倍啥,同一個(gè)host最大并發(fā)是maxRequestsPerHost(默認(rèn)值5)虽缕。
回到AsyncCall類(lèi)中查看具體的執(zhí)行過(guò)程:
它繼承自 NamedRunnable氮趋、Runnable剩胁,其run方法執(zhí)行以下代碼:
protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
先不管上面那個(gè)攔截器,可以看到大致是判斷了是否取消、是否失敗、是否有IO異常缤至,回調(diào)onfailure方法领斥,否則就回調(diào)onResponse了月洛。
finally 執(zhí)行這個(gè)方法: client.dispatcher().finished(this);到Dispatcher的finished方法中:
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
可以看到把call從runningAsyncCalls移除了。同時(shí)執(zhí)行下面的方法:
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
從readyAsyncCalls中取出一個(gè)加入到runningAsyncCalls中唬涧,同樣是經(jīng)歷上面的判斷,空閑就立刻執(zhí)行狮荔。
哦,對(duì)了受葛,執(zhí)行的executor也是放在dispatcher中的,具體初始化代碼如下:
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
可以看到是一個(gè)單例的設(shè)計(jì)模式,把synchronized關(guān)鍵字加在了方法上。該線程池的keepalivetime是60s正蛙,使用的是不存儲(chǔ)元素的阻塞隊(duì)列SynchronousQueue乒验。也就是我們最常用的ExecutorService newCachedThreadPool()的代碼實(shí)現(xiàn)狂塘。
總結(jié)一下就是:
我們得到的Call是一個(gè)RealCall荞胡,調(diào)用enqueue方法來(lái)把任務(wù)加入到Dispatcher的請(qǐng)求隊(duì)列中。
根據(jù)當(dāng)前正在進(jìn)行中的網(wǎng)絡(luò)請(qǐng)求個(gè)數(shù)和設(shè)置的最大網(wǎng)絡(luò)請(qǐng)求數(shù)的情況來(lái)決定是加入請(qǐng)求隊(duì)列還是等待隊(duì)列。
請(qǐng)求隊(duì)列中的任務(wù)每完成一個(gè)纵刘,就會(huì)自動(dòng)把等待隊(duì)列中的任務(wù)加入到請(qǐng)求隊(duì)列中。
Call的cancel方法并不是把請(qǐng)求從隊(duì)列中立即移除舵抹,而是將其標(biāo)記為已取消狀態(tài),等輪到它執(zhí)行時(shí)根據(jù)這個(gè)標(biāo)記直接回調(diào)onfailure方法。
網(wǎng)絡(luò)請(qǐng)求任務(wù)管理策略就是這樣靠娱,下一篇我們看一下okhttp的攔截器像云。