關(guān)注我废登,一個(gè)仍存夢(mèng)想的屌絲程序員,每天為你分享高質(zhì)量編程博客籍铁。
follow us for dream
前言
手把手講解系列文章,是我寫(xiě)給各位看官趾断,也是寫(xiě)給我自己的拒名。
文章可能過(guò)分詳細(xì),但是這是為了幫助到盡量多的人芋酌,畢竟工作5,6年增显,不能老吸血,也到了回饋開(kāi)源的時(shí)候.
這個(gè)系列的文章:
1脐帝、用通俗易懂的講解方式同云,講解一門(mén)技術(shù)的實(shí)用價(jià)值
2、詳細(xì)書(shū)寫(xiě)源碼的追蹤堵腹,源碼截圖炸站,繪制類的結(jié)構(gòu)圖,盡量詳細(xì)地解釋原理的探索過(guò)程
3疚顷、提供Github 的 可運(yùn)行的Demo工程,但是我所提供代碼旱易,更多是提供思路,拋磚引玉腿堤,請(qǐng)酌情cv
4阀坏、集合整理原理探索過(guò)程中的一些坑,或者demo的運(yùn)行過(guò)程中的注意事項(xiàng)
5释液、用gif圖全释,最直觀地展示demo運(yùn)行效果如果覺(jué)得細(xì)節(jié)太細(xì),直接跳過(guò)看結(jié)論即可误债。
本人能力有限浸船,如若發(fā)現(xiàn)描述不當(dāng)之處妄迁,歡迎留言批評(píng)指正。
學(xué)到老活到老李命,路漫漫其修遠(yuǎn)兮登淘。與眾君共勉 !
引子
OkHttp 知名第三方網(wǎng)絡(luò)框架SDK,使用簡(jiǎn)單封字,性能優(yōu)秀黔州,但是內(nèi)核并不簡(jiǎn)單,此系列文章阔籽,專挑硬核知識(shí)點(diǎn)詳細(xì)講解.
何為硬核流妻,就是要想深入研究,你絕對(duì)繞不過(guò)去的知識(shí)點(diǎn)
正文大綱
OkHttp是什么
OkHttp怎么用
OkHttp源碼核心類之一:分發(fā)器詳解
OkHttp源碼核心類之一:攔截器簡(jiǎn)述
正文
OkHttp是什么
OkHttp是時(shí)下非常流行的網(wǎng)絡(luò)編程框架,由行業(yè)巨佬
Square
公司開(kāi)源笆制,很多其他的流行框架比如retrofit
的底層也是okhttp绅这,只不過(guò)使用了注解反射動(dòng)態(tài)代理將其進(jìn)行了封裝。
流行版本為:3.10.0在辆,最新版本為:4.0.1证薇,只不過(guò)將實(shí)現(xiàn)語(yǔ)言從java改成了kotlin。
相對(duì)于其他網(wǎng)絡(luò)框架匆篓,有如下優(yōu)點(diǎn):
- 支持
Spdy
浑度、Http1.X
、Http2
鸦概、Quic
以及WebSocket
- 連接池復(fù)用底層
TCP(Socket)
箩张,減少請(qǐng)求延時(shí)
- 無(wú)縫的支持
GZIP
減少數(shù)據(jù)流量
- 緩存響應(yīng)數(shù)據(jù)減少重復(fù)的網(wǎng)絡(luò)請(qǐng)求
- 請(qǐng)求失敗自動(dòng)重試主機(jī)的其他
ip
,自動(dòng)重定向
OkHttp怎么用
添加gradle依賴
dependencies {
public class MyRequest {
OkHttp的簡(jiǎn)單使用方法大致使用如上窗市,其中也有一些細(xì)節(jié)需要注意:
1伏钠、在應(yīng)用層使用OkHttp,必然會(huì)涉及到4個(gè)重要元素:
- OkHttpClient類(產(chǎn)生OkHttp客戶端實(shí)例)
- Request類(請(qǐng)求封裝)
- Call類(網(wǎng)絡(luò)任務(wù)封裝,并決定是要
同步執(zhí)行
還是異步執(zhí)行
,注意谨设,同步請(qǐng)求不
可以放在主線程
中,但是異步請(qǐng)求可以 )
- Response類(網(wǎng)絡(luò)任務(wù)執(zhí)行之后的回調(diào))
2缎浇、執(zhí)行網(wǎng)絡(luò)請(qǐng)求必須在manifest中申請(qǐng)
INTERNET
權(quán)限扎拣,不然會(huì)拋異常.
3、完整的一個(gè)請(qǐng)求執(zhí)行出去素跺,流程如下圖:
imageimage.png
OkHttp源碼核心類之一:分發(fā)器詳解
上述二蓝,提到Call類,可以選擇性執(zhí)行 同步或者異步請(qǐng)求指厌,但是無(wú)論同步異步刊愚,都一定會(huì)經(jīng)過(guò)一個(gè)門(mén)戶:"分發(fā)器" :
索引進(jìn)源碼(okhttp v3.10.0):
image異步請(qǐng)求.png
image同步請(qǐng)求.png
雖然用戶不需要直接操作分發(fā)器,但是 分發(fā)器踩验,作為OkHttp
架構(gòu)的一個(gè)門(mén)戶層鸥诽,是所有請(qǐng)求的必經(jīng)之路商玫,其中的代碼還是有必要了解細(xì)節(jié)的。
同步請(qǐng)求
進(jìn)入分發(fā)器 Dispatcher
之后, 會(huì)執(zhí)行 getResponseWithInterceptorChain()
來(lái)執(zhí)行這個(gè)Call
任務(wù)牡借,得到一個(gè)Response
,其中的細(xì)節(jié)分為兩步:
1拳昌、
client.dispatcher().executed(this);
,進(jìn)入源碼可以看到 僅僅是執(zhí)行了runningSyncCalls.add(call);
钠龙,將call對(duì)象加入到了一個(gè)雙端隊(duì)列Deque<RealCall> runningSyncCalls
中炬藤。
2、getResponseWithInterceptorChain()
是執(zhí)行網(wǎng)絡(luò)請(qǐng)求的核心內(nèi)容碴里,涉及到攔截器沈矿,在這一節(jié)上暫時(shí)不詳述。
同步請(qǐng)求的執(zhí)行步驟十分簡(jiǎn)單咬腋,將任務(wù)加入到 runningSyncCalls列表羹膳,并且直接執(zhí)行核心方法,同步阻塞拿到response帝火。
異步請(qǐng)求
異步請(qǐng)求進(jìn)入分發(fā)器之后溜徙,
imageimage.png
可能會(huì)被加入到
Deque<AsyncCall> runningAsyncCalls
這么一個(gè)雙端隊(duì)列中,然后executorService().execute(call);
實(shí)際上是用了線程池來(lái)執(zhí)行了這個(gè)異步任務(wù)犀填。
但是蠢壹,請(qǐng)注意(還是剛才的enqueue方法代碼)這里有一個(gè)判斷條件 if分支 :imageimage.png
這個(gè)條件是否滿足,將會(huì)直接決定是直接執(zhí)行這個(gè)任務(wù)九巡,還是將任務(wù)加入到 readyAsyncCalls 雙端隊(duì)列.
那么設(shè)置這個(gè)條件的目的是什么呢图贸?從變量命名來(lái)看:
runningAsyncCalls 執(zhí)行中的異步任務(wù)
runningCallsForHost 同一個(gè)域名正在執(zhí)行的任務(wù)數(shù)
readyAsyncCalls 預(yù)備執(zhí)行的任務(wù)隊(duì)列(尚未執(zhí)行)
當(dāng)正在執(zhí)行的任務(wù)數(shù)小于最大值(默認(rèn)為64)并且,同一個(gè)域名正在請(qǐng)求的任務(wù)數(shù)小于最大值(默認(rèn)5)時(shí)冕广,才會(huì)立即執(zhí)行疏日,否則,這個(gè)任務(wù)會(huì)被加入到 readyAsyncCalls中等待安排撒汉。
那么問(wèn)題來(lái)了沟优,readyAsyncCalls中的任務(wù)什么時(shí)候會(huì)被執(zhí)行?
追蹤代碼:追蹤 readyAsyncCalls 的使用代碼睬辐,找到遍歷
這個(gè)隊(duì)列的地方:
imageimage.png
繼續(xù)追蹤挠阁,找到了這個(gè) finish方法:
imageimage.png
繼續(xù)追蹤finish在哪里調(diào)用的,找到兩處:
imageimage.png
imageimage.png
所以溯饵,得出結(jié)論:
在一個(gè)任務(wù)(無(wú)論同步還是異步)結(jié)束之后侵俗,分發(fā)器中的異步任務(wù),存在兩個(gè)隊(duì)列丰刊,一個(gè)running隊(duì)列
隘谣,一個(gè)ready隊(duì)列
,當(dāng)running隊(duì)列
的size小于最大值啄巧,并且同一個(gè)域名正在執(zhí)行的任務(wù)數(shù)小于最大值時(shí)寻歧,可以直接加入到running隊(duì)列掌栅,立即執(zhí)行。如果不滿足這條件熄求,這個(gè)異步任務(wù)就會(huì)被加入到 ready隊(duì)列.在任意一個(gè)任務(wù)(
無(wú)論同步或是異步任務(wù)
)執(zhí)行完畢(無(wú)論成敗
)之后渣玲,就會(huì)遍歷ready隊(duì)列
,每次從ready隊(duì)列
中取出一個(gè)任務(wù)弟晚,判斷同時(shí)執(zhí)行的異步任務(wù)數(shù)是否達(dá)到上限忘衍,并且同一主機(jī)的訪問(wèn)數(shù)是否達(dá)到上限,如果都滿足卿城,就加入到running隊(duì)列枚钓,并且立即執(zhí)行,不滿足瑟押,就停止遍歷搀捷。周而復(fù)始,直到所有的異步任務(wù)都執(zhí)行完多望。
文字不夠形象嫩舟,畫(huà)個(gè)圖表示。
image未命名文件.jpg
關(guān)于okhttp的分發(fā)器Dispatcher用到的線程池
同步請(qǐng)求怀偷,沒(méi)有用到線程池家厌。
imageimage.png
但是異步請(qǐng)求的代碼中,有這么一句椎工。
image.gifimage.png
我們知道饭于,為什么這里會(huì)用到線程池呢?
1.觀察 同步或者異步的call的實(shí)例维蒙。
imageimage.png
那么這個(gè)
Call
是什么掰吕?它是一個(gè)接口,它的唯一實(shí)現(xiàn)類是RealCall
,imageimage.png
在
RealCall
中颅痊,異步請(qǐng)求的執(zhí)行方法殖熟,enqueue()
其實(shí)是交給了 分發(fā)器一個(gè)AsyncCall
對(duì)象,它繼承自NamedRunnable
可命名的Runnable
任務(wù)斑响。所以吗讶,這里可以用 線程池ExecutorService
來(lái)執(zhí)行這個(gè)Runnable
.
進(jìn)一步觀察這個(gè)線程池的細(xì)節(jié):
imageimage.png
它是一個(gè)核心線程數(shù)為0的線程池,并且使用了一個(gè)無(wú)容量的阻塞隊(duì)列作為參數(shù)恋捆。
其實(shí)也不不必自己去創(chuàng)建線程池,而可以直接使用Executors.newCachedThreadPool();
來(lái)創(chuàng)建重绷,效果一樣沸停。
線程池,系統(tǒng)提供了有多種默認(rèn)實(shí)現(xiàn)imageimage.png
為什么okhttp偏偏選擇了這一種昭卓?
答:為了實(shí)現(xiàn)最大并發(fā)量愤钾。
詳解如下:
既然這里提到了線程池瘟滨,那么就把線程池的基本機(jī)制整理一下:
image線程池的工作流程圖.jpg
線程池的構(gòu)造函數(shù)中,有一個(gè)阻塞隊(duì)列參數(shù)能颁。
imageimage.png
它有3個(gè)實(shí)現(xiàn)類:
ArrayBlockingDeque
/LinkedBlockingDeque
/SynchronousQueue
是我們線程池經(jīng)常用的杂瘸。
前面2個(gè)都是有容量的,而第三個(gè)是無(wú)容量的伙菊,加入進(jìn)去败玉,一定會(huì)失敗。而參照上面線程池的工作流程圖镜硕,如果加入失敗运翼,就會(huì)嘗試去非核心線程執(zhí)行任務(wù)。這樣兴枯,便保證了每一個(gè)提交進(jìn)來(lái)的異步任務(wù)血淌,都會(huì)立即嘗試去執(zhí)行,而不是塞入等待隊(duì)列中等待空閑線程财剖,從而確保了 異步任務(wù)的并發(fā)悠夯。
OkHttp源碼核心類之一:攔截器簡(jiǎn)述
上面講解分發(fā)器的時(shí)候,提到了 RealCall類的getResponseWithInterceptorChain()
方法躺坟。它是一個(gè)網(wǎng)絡(luò)請(qǐng)求執(zhí)行的真正核心方法沦补。
進(jìn)入方法:
image核心方法.png
- 新建一個(gè)攔截器List,并且放入各種攔截器對(duì)象
- 將攔截器list瞳氓,交給RealInterceptorChain策彤,進(jìn)行責(zé)任鏈模式的調(diào)用,最終得出Response.
首先解釋一下責(zé)任鏈模式
匣摘,它是21種基本設(shè)計(jì)模式中店诗,行為模式中一種。下面的案例可以很好地解釋它:
image責(zé)任鏈模式案例
當(dāng)一個(gè)國(guó)企要采購(gòu)一批設(shè)備的時(shí)候音榜,按照上圖整個(gè)任務(wù)流程中庞瘸,存在5個(gè)對(duì)象,都能對(duì)采購(gòu)流程造成影響赠叼,采購(gòu)任務(wù)開(kāi)始的時(shí)候擦囊,是從上到下依次對(duì)采購(gòu)流程負(fù)責(zé)。而總經(jīng)理嘴办,他才不關(guān)心下面的人怎么操作瞬场,他只關(guān)心最后的結(jié)果。
正如此案例中所述涧郊,okhttp的責(zé)任鏈模式贯被,使用者也不需要關(guān)心這個(gè)請(qǐng)求到底經(jīng)歷了哪些過(guò)程,他只知道,我給了request彤灶,你就要給我response看幼,而過(guò)程中,發(fā)生作用的各類攔截器幌陕,無(wú)需使用者知道诵姜,這樣就達(dá)成了面向?qū)ο蟪绦蜷_(kāi)發(fā)
中的最少知道原則
。
而搏熄,這些攔截器棚唆,恰恰是okhttp的核心內(nèi)容,下篇文章將會(huì)詳細(xì)講解搬卒。
結(jié)語(yǔ)
本文是okhttp
的開(kāi)篇瑟俭,如果要詳細(xì)解讀okhttp
的每個(gè)細(xì)節(jié),每一篇文章將會(huì)顯得非常冗長(zhǎng)而且乏味
契邀,所以我選了重要節(jié)點(diǎn)著重分析摆寄。就像攻城略地打天下,先占領(lǐng)據(jù)點(diǎn)
坯门,再企圖擴(kuò)張
微饥,一步一個(gè)腳印,穩(wěn)扎穩(wěn)打古戴,才能長(zhǎng)遠(yuǎn)發(fā)展
回復(fù)關(guān)鍵字:
1欠橘、回復(fù) “10” 查看 最有價(jià)值的10個(gè)spring boot開(kāi)源項(xiàng)目
2、回復(fù) “國(guó)旗” 獲取國(guó)旗頭像教程
3现恼、回復(fù) “Ubuntu” 獲取100 個(gè)最佳 Ubuntu 應(yīng)用 和 linux神器
4肃续、回復(fù) “ idea ” 獲取**最新idea破解教程 和 裝逼神奇
5、回復(fù) “ ssh ” 獲取史上最好的 ssh工具 支持mac
6叉袍、回復(fù) “ 代金券 ” 騰訊云和阿里云代金券
7始锚、回復(fù) “免費(fèi)” 可以獲取免費(fèi)的java面試資源和學(xué)習(xí)視頻
推薦閱讀:
面試官問(wèn)我:一個(gè) TCP 連接可以發(fā)多少個(gè) HTTP 請(qǐng)求?我竟然回答不上來(lái)..
教你迅雷&百度非會(huì)員也能享受不限速的特權(quán)
Chrome開(kāi)發(fā)者工具(DevTools)使用技巧
100個(gè)最有價(jià)值的開(kāi)源項(xiàng)目--微信開(kāi)發(fā)系列
一臺(tái)Linux服務(wù)器可以負(fù)載多少個(gè)連接?(底部有福利)
免責(zé)聲明:
1.本公眾號(hào)所轉(zhuǎn)載文章均來(lái)自公開(kāi)網(wǎng)絡(luò)喳逛。
2.如果出處標(biāo)注有誤或侵犯到原著作者權(quán)益瞧捌,請(qǐng)聯(lián)系刪除。
3.轉(zhuǎn)載本公眾號(hào)中的文章請(qǐng)注明原文鏈接和作者润文,否則產(chǎn)生的任何版權(quán)糾紛均與本公眾號(hào)無(wú)關(guān)姐呐。