OkHttp(基于源碼的核心理解)

作為android著名開源廠商square的主打產(chǎn)品蒂破,OkHttp可謂是具有諸多優(yōu)點,畢竟站在巨人的肩膀上,吸取了眾多網(wǎng)絡(luò)框架的優(yōu)點恕齐,提升了穩(wěn)定性。
對于不會使用OkHttp的,推薦泡在網(wǎng)上的日子的一片博文OkHttp使用教程瞬逊。
OkHttp的調(diào)用可以簡單的分為以下幾步

  1. 生成一個OkHttpClient(用以總的控制)
  2. 用各種鍵值對包裝我們的Request
  3. 將請求加入隊列生成一個Call管理請求
  4. 若是同步請求直接執(zhí)行excute等待處理返回Response显歧,若為異步則實現(xiàn)Callback回調(diào)仪或,在onResponse里獲取Response參數(shù)

因此我們的分析也將從這幾個步驟出發(fā)。
首先對于OkHttpClient來說,OkHttp官方文檔并不建議我們創(chuàng)建多個OkHttpClient士骤,因此全局使用一個范删。 如果有需要,可以使用clone方法拷肌,再進(jìn)行自定義,類似于數(shù)據(jù)操作類到旦,采用單例模式有利于我們省卻同步問題,以及節(jié)省性能巨缘。
當(dāng)我們初始化OkHttpClient的時候添忘,其內(nèi)部先生成了一個Builder,然后初始化一系列管理器若锁,以及初始化參數(shù)

OkHttpClient的Builder初始化參數(shù)

這里我們著重分析下Dispatcher搁骑,首先我們需要了解下線程池,以及反向代理模式

線程池技術(shù)

相比我們對于異步任務(wù)的需求應(yīng)該遇到了不少又固,首先想到的便是Thread仲器,handler等異步機(jī)制,Java已經(jīng)做了很好的封裝口予,但是當(dāng)我們需要使用許多異步任務(wù)娄周,而這些任務(wù)只做了一點點事情就完成了它的使命,當(dāng)我們不斷的創(chuàng)建沪停,銷毀線程的時候煤辨,對系統(tǒng)的開銷是相當(dāng)大的,因為這些過程也是耗費時間的木张,這個時候我們就需要用到線程池了众辨,我們只要往池子里放任務(wù),他就會自動幫你管理線程的創(chuàng)建與銷毀舷礼,利用緩存復(fù)用減少線程銷毀創(chuàng)建鹃彻,優(yōu)化系統(tǒng)性能。以下是線程池的優(yōu)點:

  1. 通過對線程進(jìn)行緩存妻献,減少了創(chuàng)建銷毀的時間損失
  2. 通過控制線程數(shù)量閥值蛛株,減少了當(dāng)線程過少時帶來的CPU閑置(比如說長時間卡在I\O上了)與線程過多時對JVM的內(nèi)存與線程切換壓力
    而Dispatcher內(nèi)部的核心即線程池
線程池初始化
  • int corePoolSize: 最小并發(fā)線程數(shù),這里并發(fā)同時包括空閑與活動的線程育拨,如果是0的話谨履,空閑一段時間后所有線程將全部被銷毀。
  • int maximumPoolSize: 最大線程數(shù)熬丧,當(dāng)任務(wù)進(jìn)來時可以擴(kuò)充的線程最大值笋粟,當(dāng)大于了這個值就會根據(jù)丟棄處理機(jī)制來處理
  • long keepAliveTime: 當(dāng)線程數(shù)大于corePoolSize時,多余的空閑線程的最大存活時間,類似于HTTP中的Keep-alive
  • TimeUnit unit: 時間單位害捕,一般用秒
  • BlockingQueue workQueue: 工作隊列
  • ThreadFactory threadFactory: 單個線程的工廠绿淋,可以打Log,設(shè)置Daemon(即當(dāng)JVM退出時尝盼,線程自動結(jié)束)等
反向代理模式

為了解決單生產(chǎn)者多消費者問題吞滞,OkHttp采用了反向代理模式,來解決非阻塞东涡,高并發(fā)問題

OkHttp工作模式

Dispatch模式

  • int corePoolSize: 最小并發(fā)線程數(shù)冯吓,這里并發(fā)同時包括空閑與活動的線程倘待,如果是0的話疮跑,空閑一段時間后所有線程將全部被銷毀。
  • int maximumPoolSize: 最大線程數(shù)凸舵,當(dāng)任務(wù)進(jìn)來時可以擴(kuò)充的線程最大值祖娘,當(dāng)大于了這個值就會根據(jù)丟棄處理機(jī)制來處理
  • long keepAliveTime: 當(dāng)線程數(shù)大于corePoolSize時,多余的空閑線程的最大存活時間啊奄,類似于HTTP中的Keep-alive
  • TimeUnit unit: 時間單位渐苏,一般用秒
  • BlockingQueue workQueue: 工作隊列
  • ThreadFactory threadFactory: 單個線程的工廠,可以打Log菇夸,設(shè)置Daemon(即當(dāng)JVM退出時琼富,線程自動結(jié)束)等
    OkHttp內(nèi)Dispatcher原理
    其實當(dāng)我們調(diào)用client.newCall(request).enqueue(new callback(){...})的時候?qū)嵸|(zhì)是
Dispatcher入隊(maxRequests=64,maxRequestsPerHost=5)

此時判斷線程池內(nèi)有無空閑庄新,否則進(jìn)入等待隊列鞠眉,AsyncCall實質(zhì)是Runnable,當(dāng)任務(wù)執(zhí)行完畢后择诈,總會調(diào)用finally{
client.dispatcher().finished(this);}清除此任務(wù)械蹋。
我們回頭看看OkhttpClient初始化內(nèi)其它一些參數(shù)Expires,Cache-Control羞芍,Last-Modified哗戈,If-Modified-Since,ETag荷科,If-None-Match...這些都牽扯到緩存問題唯咬,大概對于緩存的判斷過程入下圖


瀏覽器緩存機(jī)制-[吳秦(Tyler)](http://www.cnblogs.com/skynet/)

okhttp內(nèi)部是通過轉(zhuǎn)化實現(xiàn)以上機(jī)制


okhttp內(nèi)部緩存map操作

參數(shù)解釋:
request:這些頭參數(shù)會被okhttp的流程裝置Interceptor打斷(下面會講),組裝成新的request
cacheCandidate:上次與服務(wù)器交互的緩存的Response,基于文件系統(tǒng)的Map畏浆,key為url的md5胆胰,置換算法為lru,即帶緩存Header的Response全度。

請求參數(shù)不同下返回狀況

具體內(nèi)部實現(xiàn)為CacheStrategy煮剧,有興趣的可以自己看下源碼
接下來講講上面提到的流程工具Interceptor

Interceptor

從上圖看出主要用于兩個地方,對request進(jìn)行包裝,或者對response進(jìn)行解析勉盅,Interceptor可以用來監(jiān)控log佑颇,修改請求,修改結(jié)果,還有GZIP壓縮。

對于request加工過程
  1. 加工過程是一個自增遞歸調(diào)用過程旺入,直到將攔截器全部調(diào)用完潭陪。(header加一個map就生成一個攔截器)
  2. 處理完后,獲得網(wǎng)絡(luò)請求畔派,getResponse(),將按照http協(xié)議規(guī)范重新序列化對象中的數(shù)據(jù)信息,最終為Raw文本
獲得Response后的加工

總體是一個將Socket解析為Response對象的過程

  1. 讀取Raw解藻,反序列化為Statusline對象
  2. 以Transfer-Encoding:chunked的模式傳輸并組裝Body(此處不太明白)
    接下來看看Call,Call相對比較簡單是個接口葡盗,RealCall為其實現(xiàn)類主要實現(xiàn)一個橋梁作用螟左,連接okhttpclient與request最后我們要進(jìn)行真正的網(wǎng)絡(luò)請求了,就要涉及到網(wǎng)絡(luò)路徑選擇觅够,這又是okhttp的另一大優(yōu)勢
    原來的http鏈接我們知道是單次傳輸胶背,結(jié)束后就不再通信了,對于網(wǎng)絡(luò)請求比較頻繁的操作喘先,這種方式貌似比較浪費時間钳吟,中間需要不斷的建立連接,因此我們需要socket那種一直保持連接的方式窘拯,所幸Http有keepalive屬性红且,直接保持連接

OkHttp中會保持5個并發(fā),時間為5分鐘树枫,當(dāng)然這些數(shù)字是經(jīng)過考量直焙,測試得出的

  1. 首先因為服務(wù)器的帶寬是有限定的,他能提供的服務(wù)也是有限的砂轻,就像銀行辦事奔誓,可以開很多個窗口,但是銀行員工就那么幾個搔涝,你要是站著茅坑不拉屎厨喂,勢必影響整個銀行的辦事效率
  2. 服務(wù)器/防火墻一般都會有并發(fā)限制,畢竟這個就跟硬件搭界了
  3. 當(dāng)然不排除黑客用僵尸連接一直占著你服務(wù)器資源

這里對于連接的控制主要由Connection(連接主要對象)庄呈,StreamAllocation計數(shù)器(實現(xiàn)回收策略關(guān)鍵)蜕煌,ConnectionPool連接池以及Deque構(gòu)成

  1. 首先Connection是個接口,他的實現(xiàn)類是RealConnection诬留,這個對象保存了連接的信息斜纪,比如贫母,核心Socket對象,Handshake握手信息盒刚,Protocol信息...是整個請求的核心部分腺劣,也是管理單元
  2. 其次StreamAllocation是個計數(shù)器,用來記錄Connection被引用的次數(shù)因块,以便我們在清理整個ConnectionPool的時候可根據(jù)最少使用原則橘原,將引用置換或者清理
  3. Dequeue用來存放RealConnection
  4. ConnectionPool是管理Connection的集合,提供類基本的管理類該有的功能涡上,其中包含一個線程池來定時執(zhí)行清理任務(wù)cleanup(long now)具體清理過程有別于GC的線索查找趾断,即上述的計數(shù)器原理,而cleanup內(nèi)又有pruneAndGetAllocationCount函數(shù)用來掃描當(dāng)前Dequeue內(nèi)Connection的狀況吩愧,并返回等待時間(主要條件即空閑Socket>5&&keepalive<5)

由于個人項目網(wǎng)絡(luò)緩存是自己寫的芋酌,更好控制,okhttp的緩存策略也就不深入研究了耻警,不過大家可以了解下Okio這個庫隔嫡,是個對文件I/O封裝很好的庫甸怕,如果有興趣的同學(xué)可以參考
文/BlackSwift(簡書作者)
原文鏈接:http://www.reibang.com/p/aad5aacd79bf
再次感謝該作者的4篇文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末甘穿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子梢杭,更是在濱河造成了極大的恐慌温兼,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件武契,死亡現(xiàn)場離奇詭異募判,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)咒唆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門届垫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人全释,你說我怎么就攤上這事装处。” “怎么了浸船?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵妄迁,是天一觀的道長。 經(jīng)常有香客問我李命,道長登淘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任封字,我火速辦了婚禮黔州,結(jié)果婚禮上耍鬓,老公的妹妹穿的比我還像新娘。我一直安慰自己流妻,他們只是感情好界斜,可當(dāng)我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著合冀,像睡著了一般各薇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上君躺,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天峭判,我揣著相機(jī)與錄音,去河邊找鬼棕叫。 笑死林螃,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俺泣。 我是一名探鬼主播疗认,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伏钠!你這毒婦竟也來了横漏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤熟掂,失蹤者是張志新(化名)和其女友劉穎缎浇,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赴肚,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡素跺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了誉券。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片指厌。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖踊跟,靈堂內(nèi)的尸體忽然破棺而出踩验,到底是詐尸還是另有隱情,我是刑警寧澤琴锭,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布晰甚,位于F島的核電站,受9級特大地震影響决帖,放射性物質(zhì)發(fā)生泄漏厕九。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一地回、第九天 我趴在偏房一處隱蔽的房頂上張望扁远。 院中可真熱鬧俊鱼,春花似錦、人聲如沸畅买。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谷羞。三九已至帝火,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間湃缎,已是汗流浹背犀填。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留嗓违,地道東北人九巡。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像蹂季,于是被迫代替她去往敵國和親冕广。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,658評論 2 350

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