RxJava+Retrofit+OKHttp源碼理解

主流網(wǎng)絡(luò)層的模式是RxJava+Retrofit+OKHttp,所以我開始研究這三個(gè)項(xiàng)目的源代碼劈猪,淺嘗理解唬渗。
文章的主要內(nèi)容如下:

1.OkHttp簡(jiǎn)介
2.OkHttp使用
3.OkHttp流程源碼跟蹤

一、OKHTTP簡(jiǎn)介

1.支持HTTP2/SPDY
2.socket自動(dòng)選擇最好路線痊远,并支持自動(dòng)重連
3.擁有自動(dòng)維護(hù)的socket連接池苛吱,減少握手次數(shù)
4.擁有隊(duì)列線程池,輕松寫并發(fā)
5.擁有Interceptors輕松處理請(qǐng)求與響應(yīng)(比如透明GZIP壓縮)基于Headers的緩存策略

二阶剑、OKHTTP使用:

1跃巡、GET請(qǐng)求

1529925916(1).jpg

2危号、POST請(qǐng)求


image.png

三、OKHTTP源碼流程分析

(一)素邪、OKHTTP 同步請(qǐng)求debug代碼跟蹤:


image.png

從上面代碼所示外莲,先是new了一個(gè)OKHttpClient對(duì)象。

1、OKHttpClient類詳解
OKHttpClient類就比較簡(jiǎn)單了:

  • 1偷线、里面包含了很多對(duì)象磨确,其實(shí)OKhttp的很多功能模塊都包裝進(jìn)這個(gè)類,讓這個(gè)類單獨(dú)提供對(duì)外的API声邦,這種外觀模式的設(shè)計(jì)十分的優(yōu)雅乏奥。外觀模式。* 2亥曹、而內(nèi)部模塊比較多邓了,就使用了Builder模式(建造器模式)。Builder模式(建造器模式)* 3媳瞪、它的方法只有一個(gè):newCall.返回一個(gè)Call對(duì)象(一個(gè)準(zhǔn)備好了的可以執(zhí)行和取消的請(qǐng)求)骗炉。

而大家仔細(xì)讀源碼又會(huì)發(fā)現(xiàn)構(gòu)造了OKHttpClient后又new了一個(gè)Rquest對(duì)象。那么咱們就來(lái)看下Request蛇受,說(shuō)道Request又不得不提Response句葵。所以咱們一起講了

2、Request兢仰、Response類詳解

Request Response

  • 1乍丈、Request、Response分別抽象成請(qǐng)求和相應(yīng)* 2旨别、其中Request包括Headers和RequestBody诗赌,而RequestBody是abstract的,他的子類是有FormBody (表單提交的)和 MultipartBody(文件上傳)秸弛,分別對(duì)應(yīng)了兩種不同的MIME類型
    FormBody :"application/x-www-form-urlencoded"
    MultipartBody:"multipart/"+xxx.* 3铭若、其中Response包括Headers和RequestBody,而ResponseBody是abstract的递览,所以他的子類也是有兩個(gè):RealResponseBody和CacheResponseBody,分別代表真實(shí)響應(yīng)和緩存響應(yīng)叼屠。* 4、由于RFC協(xié)議規(guī)定绞铃,所以所有的頭部信息不是隨便寫的镜雨,request的header與response的header的標(biāo)準(zhǔn)都不同。具體的見(jiàn) List of HTTP header fields儿捧。OKHttp的封裝類Request和Response為了應(yīng)用程序編程方便荚坞,會(huì)把一些常用的Header信息專門提取出來(lái),作為局部變量菲盾。比如contentType颓影,contentLength,code,message,cacheControl,tag...它們其實(shí)都是以name-value對(duì)的形勢(shì)懒鉴,存儲(chǔ)在網(wǎng)絡(luò)請(qǐng)求的頭部信息中诡挂。

根據(jù)從上面的GET請(qǐng)求碎浇,顯示用builder構(gòu)建了Request對(duì)象,然后執(zhí)行了OKHttpClient.java的newCall方法璃俗,那么咱們就看看這個(gè)newCall里面都做什么操作奴璃?


image.png

Call是個(gè)什么東西,那咱們看下Call這個(gè)類

Call: HTTP請(qǐng)求任務(wù)封裝
可以說(shuō)我們能用到的操縱基本上都定義在這個(gè)接口里面了城豁,所以也可以說(shuō)這個(gè)類是OKHttp類的核心類了苟穆。我們可以通過(guò)Call對(duì)象來(lái)操作請(qǐng)求了。而Call接口內(nèi)部提供了Factory工廠方法模式(將對(duì)象的創(chuàng)建延遲到工廠類的子類去進(jìn)行唱星,從而實(shí)現(xiàn)動(dòng)態(tài)配置)
Call接口提供了內(nèi)部接口Factory(用于將對(duì)象的創(chuàng)建延遲到該工廠類的子類中進(jìn)行鞭缭,從而實(shí)現(xiàn)動(dòng)態(tài)的配置).

image.png

在源碼中,OKHttpClient實(shí)現(xiàn)了Call.Factory接口魏颓,返回了一個(gè)RealCall對(duì)象岭辣。那我們就來(lái)看下RealCall這個(gè)類

4、RealCall類詳解
RealCall

1甸饱、OkHttpClient的newCall方法里面new了RealCall的對(duì)象沦童,但是RealCall的構(gòu)造函數(shù)需要傳入一個(gè)OKHttpClient對(duì)象和Request對(duì)象(PS:第三個(gè)參數(shù)false表示不是webSokcet).因此RealCall包裝了Request對(duì)象。所以RealCall可以很方便地使用這兩個(gè)對(duì)象叹话。
2偷遗、RealCall里面的兩個(gè)關(guān)鍵方法是:execute 和 enqueue。分別用于同步和異步得執(zhí)行網(wǎng)絡(luò)請(qǐng)求驼壶。
3氏豌、RealCall還有一個(gè)重要方法是:getResponseWithInterceptorChain,添加攔截器热凹,通過(guò)攔截器可以將一個(gè)流式工作分解為可配置的分段流程泵喘,既增加了靈活性也實(shí)現(xiàn)了解耦,關(guān)鍵還可以自有配置般妙,非常完美纪铺。

所以client.newCall(request).execute();實(shí)際上執(zhí)行的是RealCall的execute方法,現(xiàn)在咱們?cè)倩貋?lái)看下RealCall的execute的具體實(shí)現(xiàn)

image.png

首先是


image.png

判斷call是否執(zhí)行過(guò)碟渺,可以看出每個(gè)Call對(duì)象只能使用一次原則鲜锚。然后調(diào)用了captureCallStackTrace()方法。
RealCall.java


image.png

RealCall的captureCallStackTrace() 又調(diào)用了Platform.get().getStackTraceForCloseable()
image.png

其實(shí)是調(diào)用AndroidPlatform. getStackTraceForCloseable(String closer)方法苫拍。這里就不詳細(xì)說(shuō)了芜繁,后面詳細(xì)說(shuō)。
然后retryAndFollowUpInterceptor.setCallStackTrace(),在這個(gè)方法里面什么都沒(méi)做就是set一個(gè)object進(jìn)去

image.png

綜上所示captureCallStackTrace()這個(gè)方法其實(shí)是捕獲了這個(gè)請(qǐng)求的StackTrace绒极。
然后進(jìn)入了第一個(gè)核心類---Dispatcher的的execute方法了骏令,由于下面是進(jìn)入了關(guān)鍵部分,所以重點(diǎn)講解下集峦,代碼如何:


image.png

看下OKHttpClient的dispatcher()方法的具體內(nèi)容如下圖


image.png

大家發(fā)現(xiàn)client.dispatcher()返回的是Dispatcher對(duì)象伏社,那么這個(gè)Dispatcher對(duì)象是何時(shí)創(chuàng)建的那?在OkHttpClient.java里面Build類里面的構(gòu)造函數(shù)里面塔淤,如下圖
image.png

所以默認(rèn)執(zhí)行Builder()放到時(shí)候就創(chuàng)建了一個(gè)Dispatcher摘昌。那么咱們看下dispatcher里面的execute()是如何處理的
image.png

里面發(fā)現(xiàn)是runningSyncCalls執(zhí)行了add方法莫非runningSyncCalls是個(gè)list,咱們查看dispatcher里面怎么定義runningSyncCalls的高蜂。


image.png

原來(lái)runningSyncCalls是雙向隊(duì)列啊聪黎,突然發(fā)現(xiàn)Dispatcher里面定義了三個(gè)雙向隊(duì)列,看下注釋备恤,我們大概能明白readyAsyncCalls 是一個(gè)存放了等待執(zhí)行任務(wù)Call的雙向隊(duì)列稿饰,runningAsyncCalls是一個(gè)存放異步請(qǐng)求任務(wù)Call的雙向任務(wù)隊(duì)列,runningSyncCalls是一個(gè)存放同步請(qǐng)求的雙向隊(duì)列露泊。關(guān)于隊(duì)列咱們?cè)谙缕恼吕锩嬖敿?xì)介紹喉镰。

執(zhí)行完client.dispatcher().executed(this);要走到getResponseWithInterceptorChain();方法了里面了,看下這個(gè)方法是具體做什么的惭笑?


image.png

發(fā)現(xiàn) new了一個(gè)ArrayList侣姆,然后就是不斷的add,后面 new了 RealInterceptorChain對(duì)象沉噩,最后調(diào)用了chain.proceed()方法捺宗。先看下RealInterceptorChain的構(gòu)造函數(shù)。


image.png

發(fā)現(xiàn)什么都沒(méi)做就是做了賦值操作川蒙,后面跟蹤下chain.proceed()方法
由于Interceptor是個(gè)接口蚜厉,所以應(yīng)該是具體實(shí)現(xiàn)類RealInterceptorChain的proceed實(shí)現(xiàn)
image.png

image.png

由于在構(gòu)造RealInterceptorChain對(duì)象時(shí)候httpCodec直接賦予了null,所以下面代碼直接略過(guò)畜眨。


image.png

然后看到在proceed方面里面又new了一個(gè)RealInterceptorChain類的next對(duì)象昼牛,溫馨提示下,里面的streamAllocation, httpCodec, connection都是null康聂,所以這個(gè)next對(duì)象和chain最大的區(qū)別就是index屬性值不同chain是0.而next是1匾嘱,然后取interceptors下標(biāo)為1的對(duì)象的interceptor。由從上文可知早抠,如果沒(méi)有開發(fā)者自定義的Interceptor時(shí)霎烙,首先調(diào)用的RetryAndFollowUpInterceptor,如果有開發(fā)者自己定義的interceptor則調(diào)用開發(fā)者interceptor蕊连。

這里重點(diǎn)說(shuō)一下悬垃,由于后面的interceptor比較多,且涉及的也是重要的部分甘苍,而咱們這里主要是講流程尝蠕,所以這里就不詳細(xì)和大家說(shuō)了,由后面再詳細(xì)講解载庭,后面的流程是在每一個(gè)interceptor的intercept方法里面都會(huì)調(diào)用chain.proceed()從而調(diào)用下一個(gè)interceptor的intercept(next)方法看彼,這樣就可以實(shí)現(xiàn)遍歷getResponseWithInterceptorChain里面interceptors的item廊佩,實(shí)現(xiàn)遍歷循環(huán),縮減后的代碼如下:


image.png

image.png

image.png

image.png

讀過(guò)源碼我們知道getResponseWithInterceptorChain里面interceptors的最后一個(gè)item是CallServerInterceptor.java靖榕,最后一個(gè)Interceptor(即CallServerInterceptor)里面是直接返回了response 而不是進(jìn)行繼續(xù)遞歸标锄,具體里面是通過(guò)OKio實(shí)現(xiàn)的,具體代碼茁计,等后面再詳細(xì)說(shuō)明料皇,CallServerInterceptor返回response后返回給上一個(gè)interceptor,一般是開發(fā)者自己定義的networkInterceptor,然后開發(fā)者自己的networkInterceptor把他的response返回給前一個(gè)interceptor星压,依次以此類推返回給第一個(gè)interceptor践剂,這時(shí)候又回到了realCall里面的execute()里面了,代碼如下:

image.png

(二)娜膘、OKHTTP 異步請(qǐng)求debug代碼跟蹤:


image.png

前面和同步一樣new了一個(gè)OKHttp和Request逊脯。這塊和同步一樣就不說(shuō)了,那么說(shuō)說(shuō)和同步不一樣的地方竣贪,后面異步進(jìn)入enqueue()方法


image.png

由于executed默認(rèn)為false男窟,所以先進(jìn)行判斷是否為true,為true則直接跑異常贾富,沒(méi)有則設(shè)置為true歉眷,可以看出executed這個(gè)是一個(gè)標(biāo)志,標(biāo)志這個(gè)請(qǐng)求是否已經(jīng)正在請(qǐng)求中颤枪,合同步一樣先調(diào)用了captureCallStackTrace();然后調(diào)用 client.dispatcher().enqueue(new AsyncCall(responseCallback));client.dispatcher()返回的是Dispatcher對(duì)象所以實(shí)際調(diào)用的是Dispatcher的enqueue(),那么咱們進(jìn)入源碼看下
image.png

根據(jù)源碼和注釋大家可以看到如果正在執(zhí)行的異步請(qǐng)求小于64汗捡,并且請(qǐng)求同一個(gè)主機(jī)小于5的時(shí)候就先往正在運(yùn)行的隊(duì)列里面添加這個(gè)call,然后用線程池去執(zhí)行這個(gè)call,否則就把他放到等待隊(duì)列里面畏纲。執(zhí)行這個(gè)call的時(shí)候扇住,自然會(huì)去走到這個(gè)call的run方法,那么咱們看下AsyncCall.java這個(gè)類,而AsyncCall.java又繼承自NamedRunnable.java咱們就一起看下他們的源碼

image.png

image.png

上面看到NamedRunnable的構(gòu)造方法設(shè)置了name在的run方法里面設(shè)定為當(dāng)前線程的name,而NamedRunnable的run方法里面調(diào)用了它自己的抽象方法execute盗胀,由此可見(jiàn)NamedRunnable的作用就是設(shè)置了線程的name艘蹋,然后回調(diào)子類的execute方法,那么我們來(lái)看下AsyncCall的execute方法票灰。貌似好像又回到了之前同步的getResponseWithInterceptorChain()里面女阀,根據(jù)返回的response來(lái)這只callback回調(diào)。所以我們得到了OKHTTP的大體流程屑迂,如下圖:

三浸策、OKHTTP類詳解
整體的流程圖


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市惹盼,隨后出現(xiàn)的幾起案子庸汗,更是在濱河造成了極大的恐慌,老刑警劉巖手报,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚯舱,死亡現(xiàn)場(chǎng)離奇詭異改化,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)枉昏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門陈肛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人凶掰,你說(shuō)我怎么就攤上這事◎谀叮” “怎么了懦窘?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)稚配。 經(jīng)常有香客問(wèn)我畅涂,道長(zhǎng),這世上最難降的妖魔是什么道川? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任午衰,我火速辦了婚禮,結(jié)果婚禮上冒萄,老公的妹妹穿的比我還像新娘臊岸。我一直安慰自己,他們只是感情好尊流,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布帅戒。 她就那樣靜靜地躺著,像睡著了一般崖技。 火紅的嫁衣襯著肌膚如雪逻住。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天迎献,我揣著相機(jī)與錄音瞎访,去河邊找鬼。 笑死吁恍,一個(gè)胖子當(dāng)著我的面吹牛扒秸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播冀瓦,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼鸦采,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了咕幻?” 一聲冷哼從身側(cè)響起渔伯,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肄程,沒(méi)想到半個(gè)月后锣吼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體选浑,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年玄叠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了古徒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡读恃,死狀恐怖隧膘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寺惫,我是刑警寧澤疹吃,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站西雀,受9級(jí)特大地震影響萨驶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜艇肴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一腔呜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧再悼,春花似錦核畴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至娘侍,卻和暖如春咖刃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背憾筏。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工嚎杨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人氧腰。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓枫浙,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親古拴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子箩帚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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