OKHttp源碼解析(三)——分發(fā)器Dispatcher

學(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ù)線程池

Dispatcher

readyCalls:待執(zhí)行異步任務(wù)隊(duì)列

runningCalls:運(yùn)行中異步任務(wù)隊(duì)列

executedCalls:運(yùn)行中同步任務(wù)隊(duì)列

executorService:任務(wù)隊(duì)列線程池

ThreadPoolExecutor

(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é)束)等

構(gòu)建ThreadPoolExecutor

可以看出仗嗦,在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)閉所有線程踊东。

分發(fā)器執(zhí)行圖

同步:

OkHttpClient client =newOkHttpClient();Requestrequest=newRequest.Builder()

.url("http://publicobject.com/helloworld.txt")

.build();Responseresponse= client.newCall(request).execute();

其中最后的call.execute();我們來看一下同步中的execute()方法:

(PS:代碼為3.7版本)

RealCall.execute

重點(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()方法了

execute

當(dāng)任務(wù)執(zhí)行完成后鉴逞,無論成功與否都會(huì)調(diào)用dispatcher.finished方法,通知分發(fā)器相關(guān)任務(wù)已結(jié)束:

finish

空閑出多余線程司训,調(diào)用promoteCalls調(diào)用待執(zhí)行的任務(wù)

如果當(dāng)前整個(gè)線程池都空閑下來构捡,執(zhí)行空閑通知回調(diào)線程(idleCallback)

接下來看看promoteCalls:

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ì)列

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市腕够,隨后出現(xiàn)的幾起案子级乍,更是在濱河造成了極大的恐慌,老刑警劉巖帚湘,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玫荣,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡大诸,警方通過查閱死者的電腦和手機(jī)捅厂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來资柔,“玉大人焙贷,你說我怎么就攤上這事』哐撸” “怎么了辙芍?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我故硅,道長(zhǎng)庶灿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任吃衅,我火速辦了婚禮往踢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘徘层。我一直安慰自己峻呕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布趣效。 她就那樣靜靜地躺著山上,像睡著了一般。 火紅的嫁衣襯著肌膚如雪英支。 梳的紋絲不亂的頭發(fā)上佩憾,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音干花,去河邊找鬼妄帘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛池凄,可吹牛的內(nèi)容都是我干的抡驼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肿仑,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼致盟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尤慰,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤馏锡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后伟端,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杯道,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年责蝠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了党巾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡霜医,死狀恐怖齿拂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肴敛,我是刑警寧澤署海,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響叹侄,放射性物質(zhì)發(fā)生泄漏巩搏。R本人自食惡果不足惜昨登,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一趾代、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丰辣,春花似錦撒强、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至琐凭,卻和暖如春芽隆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背统屈。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工胚吁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人愁憔。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓腕扶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親吨掌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子半抱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容