手把手講解 OkHttp硬核知識(shí)點(diǎn)(1)

image

關(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.XHttp2鸦概、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í)行出去素跺,流程如下圖:

image

image.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ā)器之后溜徙,

image

image.png

可能會(huì)被加入到 Deque<AsyncCall> runningAsyncCalls 這么一個(gè)雙端隊(duì)列中,然后 executorService().execute(call);實(shí)際上是用了線程池來(lái)執(zhí)行了這個(gè)異步任務(wù)犀填。
但是蠢壹,請(qǐng)注意(還是剛才的enqueue方法代碼)這里有一個(gè)判斷條件 if分支 :

image

image.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ì)列的地方:

image

image.png

繼續(xù)追蹤挠阁,找到了這個(gè) finish方法:

image

image.png

繼續(xù)追蹤finish在哪里調(diào)用的,找到兩處:

image

image.png

image

image.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)有用到線程池家厌。

image

image.png

但是異步請(qǐng)求的代碼中,有這么一句椎工。

image.gif

image.png

我們知道饭于,為什么這里會(huì)用到線程池呢?

1.觀察 同步或者異步的call的實(shí)例维蒙。

image

image.png

那么這個(gè)Call是什么掰吕?它是一個(gè)接口,它的唯一實(shí)現(xiàn)類是RealCall,

image

image.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é):

image

image.png

它是一個(gè)核心線程數(shù)為0的線程池,并且使用了一個(gè)無(wú)容量的阻塞隊(duì)列作為參數(shù)恋捆。
其實(shí)也不不必自己去創(chuàng)建線程池,而可以直接使用Executors.newCachedThreadPool(); 來(lái)創(chuàng)建重绷,效果一樣沸停。
線程池,系統(tǒng)提供了有多種默認(rèn)實(shí)現(xiàn)

image

image.png

為什么okhttp偏偏選擇了這一種昭卓?

答:為了實(shí)現(xiàn)最大并發(fā)量愤钾。

詳解如下:
既然這里提到了線程池瘟滨,那么就把線程池的基本機(jī)制整理一下:

image

線程池的工作流程圖.jpg

線程池的構(gòu)造函數(shù)中,有一個(gè)阻塞隊(duì)列參數(shù)能颁。

image

image.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í)視頻

image

推薦閱讀:

騰訊電話面試總結(jié)—Linux運(yùn)維工程師

python爬蟲(chóng):(嘿嘿嘿)爬你喜歡的照片

面試官問(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ā)系列

IDEA 2019 最新激活教程

一臺(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)姐呐。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市典蝌,隨后出現(xiàn)的幾起案子曙砂,更是在濱河造成了極大的恐慌,老刑警劉巖骏掀,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件麦轰,死亡現(xiàn)場(chǎng)離奇詭異乔夯,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)款侵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)侧纯,“玉大人新锈,你說(shuō)我怎么就攤上這事】舭荆” “怎么了妹笆?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)娜氏。 經(jīng)常有香客問(wèn)我拳缠,道長(zhǎng),這世上最難降的妖魔是什么贸弥? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任窟坐,我火速辦了婚禮,結(jié)果婚禮上绵疲,老公的妹妹穿的比我還像新娘哲鸳。我一直安慰自己,他們只是感情好盔憨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布徙菠。 她就那樣靜靜地躺著,像睡著了一般郁岩。 火紅的嫁衣襯著肌膚如雪婿奔。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天问慎,我揣著相機(jī)與錄音萍摊,去河邊找鬼。 笑死蝴乔,一個(gè)胖子當(dāng)著我的面吹牛记餐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播薇正,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼片酝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了挖腰?” 一聲冷哼從身側(cè)響起雕沿,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎猴仑,沒(méi)想到半個(gè)月后审轮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體肥哎,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年疾渣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了篡诽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡榴捡,死狀恐怖杈女,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吊圾,我是刑警寧澤达椰,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站项乒,受9級(jí)特大地震影響啰劲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜檀何,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一蝇裤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧埃碱,春花似錦猖辫、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至似炎,卻和暖如春辛萍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背羡藐。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工贩毕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仆嗦。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓辉阶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親瘩扼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谆甜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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