學(xué)習(xí)參考資料:OKHttp源碼解析OKHttp源碼分析——攔截器OKhttp完全解析-攔截器
一、概括
說到OKHttp請(qǐng)求的同步和異步,就要提到Dispatcher分發(fā)器了,根據(jù)前兩篇的源碼分析凶硅,可以知道在發(fā)起請(qǐng)求時(shí),整個(gè)框架主要通過Call來封裝每一次的請(qǐng)求。同時(shí)Call持有OkHttpClient和一份HttpEngine跌造。而每一次的同步或者異步請(qǐng)求都會(huì)有Dispatcher的參與,不同的是:
同步
Dispatcher會(huì)在同步執(zhí)行任務(wù)隊(duì)列中記錄當(dāng)前被執(zhí)行過得任務(wù)Call族购,同時(shí)在當(dāng)前線程中去執(zhí)行Call的getResponseWithInterceptorChain()方法壳贪,直接獲取當(dāng)前的返回?cái)?shù)據(jù)Response;
異步
首先來說一下Dispatcher寝杖,Dispatcher內(nèi)部實(shí)現(xiàn)了懶加載無邊界限制的線程池方式违施,同時(shí)該線程池采用了 SynchronousQueue這種阻塞隊(duì)列。SynchronousQueue每個(gè)插入操作必須等待另一個(gè)線程的移除操作瑟幕,同樣任何一個(gè)移除操作都等 待另一個(gè)線程的插入操作磕蒲。因此此隊(duì)列內(nèi)部其 實(shí)沒有任何一個(gè)元素,或者說容量是0只盹,嚴(yán)格說并不是一種容器辣往。由于隊(duì)列沒有容量,因此不能調(diào)用peek操作殖卑,因?yàn)橹挥幸瞥貢r(shí)才有元素站削。顯然這是一種快 速傳遞元素的方式,也就是說在這種情況下元素總是以最快的方式從插入者(生產(chǎn)者)傳遞給移除者(消費(fèi)者)孵稽,這在多任務(wù)隊(duì)列中是最快處理任務(wù)的方式许起。對(duì)于高 頻繁請(qǐng)求的場(chǎng)景十偶,無疑是最適合的。
異步執(zhí)行是通過Call.enqueue(Callback responseCallback)來執(zhí)行园细,在Dispatcher中添加一個(gè)封裝了Callback的Call的匿名內(nèi)部類Runnable來執(zhí)行當(dāng)前 的Call扯键。這里一定要注意的地方這個(gè)AsyncCall是Call的匿名內(nèi)部類。AsyncCall的execute方法仍然會(huì)回調(diào)到Call的 getResponseWithInterceptorChain方法來完成請(qǐng)求珊肃,同時(shí)將返回?cái)?shù)據(jù)或者狀態(tài)通過Callback來完成荣刑。
二、源碼分析
OkHttp的任務(wù)隊(duì)列主要由兩部分組成:
1. 任務(wù)分發(fā)器dispatcher:負(fù)責(zé)為任務(wù)找到合適的執(zhí)行線程 ?2.網(wǎng)絡(luò)請(qǐng)求任務(wù)線程池
readyCalls:待執(zhí)行異步任務(wù)隊(duì)列
runningCalls:運(yùn)行中異步任務(wù)隊(duì)列
executedCalls:運(yùn)行中同步任務(wù)隊(duì)列
executorService:任務(wù)隊(duì)列線程池
(PS:這里附上的代碼為OKHttp2.5.0伦乔,后面有改動(dòng)的源碼厉亏,會(huì)附上3.7版本的代碼,整體并不影響學(xué)習(xí))
int corePoolSize: 最小并發(fā)線程數(shù)烈和,這里并發(fā)同時(shí)包括空閑與活動(dòng)的線程爱只,如果是0的話,空閑一段時(shí)間后所有線程將全部被銷毀
int maximumPoolSize: 最大線程數(shù)招刹,當(dāng)任務(wù)進(jìn)來時(shí)可以擴(kuò)充的線程最大值恬试,當(dāng)大于了這個(gè)值就會(huì)根據(jù)丟棄處理機(jī)制來處理
long keepAliveTime: 當(dāng)線程數(shù)大于corePoolSize時(shí),多余的空閑線程的最大存活時(shí)間疯暑,類似于HTTP中的Keep-alive
TimeUnit unit: 時(shí)間單位训柴,一般用秒
BlockingQueue workQueue: 工作隊(duì)列,先進(jìn)先出妇拯,可以看出并不像Picasso那樣設(shè)置優(yōu)先隊(duì)列
ThreadFactory threadFactory: 單個(gè)線程的工廠幻馁,可以打Log,設(shè)置Daemon(即當(dāng)JVM退出時(shí)越锈,線程自動(dòng)結(jié)束)等
可以看出仗嗦,在Okhttp中,構(gòu)建了一個(gè)閥值為[0, Integer.MAX_VALUE]的線程池甘凭,它不保留任何最小線程數(shù)稀拐,隨時(shí)創(chuàng)建更多的線程數(shù),當(dāng)線程空閑時(shí)只能活60秒丹弱,它使用了一個(gè)不存儲(chǔ)元素的阻塞工作隊(duì)列德撬,一個(gè)叫做"OkHttp Dispatcher"的線程工廠。
也就是說蹈矮,在實(shí)際運(yùn)行中砰逻,當(dāng)收到10個(gè)并發(fā)請(qǐng)求時(shí)鸣驱,線程池會(huì)創(chuàng)建十個(gè)線程泛鸟,當(dāng)工作完成后,線程池會(huì)在60s后相繼關(guān)閉所有線程踊东。
同步:
OkHttpClient client =newOkHttpClient();Requestrequest=newRequest.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();Responseresponse= client.newCall(request).execute();
其中最后的call.execute();我們來看一下同步中的execute()方法:
(PS:代碼為3.7版本)
重點(diǎn)為:
client.dispatcher().executed(this);
client.dispatcher().finished(this);
同步調(diào)用的執(zhí)行邏輯是:1.將對(duì)應(yīng)任務(wù)加入分發(fā)器?2.執(zhí)行任務(wù) 3.執(zhí)行完成后通知dispatcher對(duì)應(yīng)任務(wù)已完成北滥,對(duì)應(yīng)任務(wù)出隊(duì)
異步:
OkHttpClient client =newOkHttpClient();Requestrequest=newRequest.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
client.newCall(request).enqueue(newCallback() {
@Overridepublicvoid onFailure(Callcall, IOException e) {Log.d("OkHttp","Call Failed:"+ e.getMessage());
}
@Overridepublicvoid onResponse(Callcall,Responseresponse) throws IOException {Log.d("OkHttp","Call succeeded:"+response.message());
}
});
異步中的call.enqueue(new Callback(){})
當(dāng)HttpClient的請(qǐng)求入隊(duì)時(shí)刚操,根據(jù)代碼,我們可以發(fā)現(xiàn)實(shí)際上是Dispatcher進(jìn)行了入隊(duì)操作再芋。
如果滿足條件:
當(dāng)前請(qǐng)求數(shù)小于最大請(qǐng)求數(shù)(64)
對(duì)單一host的請(qǐng)求小于閾值(5)
將該任務(wù)插入正在執(zhí)行任務(wù)隊(duì)列菊霜,并執(zhí)行對(duì)應(yīng)任務(wù)。如果不滿足則將其放入待執(zhí)行隊(duì)列济赎。
從之前的筆記中已經(jīng)看過AsyncCall的execute()方法了
當(dāng)任務(wù)執(zhí)行完成后鉴逞,無論成功與否都會(huì)調(diào)用dispatcher.finished方法,通知分發(fā)器相關(guān)任務(wù)已結(jié)束:
空閑出多余線程司训,調(diào)用promoteCalls調(diào)用待執(zhí)行的任務(wù)
如果當(dāng)前整個(gè)線程池都空閑下來构捡,執(zhí)行空閑通知回調(diào)線程(idleCallback)
接下來看看promoteCalls:
promoteCalls的邏輯也很簡(jiǎn)單:掃描待執(zhí)行任務(wù)隊(duì)列,將任務(wù)放入正在執(zhí)行任務(wù)隊(duì)列壳猜,并執(zhí)行該任務(wù)勾徽。
三、 總結(jié)
以上就是整個(gè)任務(wù)隊(duì)列的實(shí)現(xiàn)細(xì)節(jié)统扳,總結(jié)起來有以下幾個(gè)特點(diǎn):
OkHttp采用Dispatcher技術(shù)喘帚,類似于Nginx,與線程池配合實(shí)現(xiàn)了高并發(fā)咒钟,低阻塞的運(yùn)行
Okhttp采用Deque作為緩存吹由,按照入隊(duì)的順序先進(jìn)先出
OkHttp最出彩的地方就是在try/finally中調(diào)用了finished函數(shù),可以主動(dòng)控制等待隊(duì)列的移動(dòng)朱嘴,而不是采用鎖或者wait/notify溉知,極大減少了編碼復(fù)雜性
講解及圖片來源OKHttp源碼分析——任務(wù)隊(duì)列