作為android著名開源廠商square的主打產(chǎn)品蒂破,OkHttp可謂是具有諸多優(yōu)點,畢竟站在巨人的肩膀上,吸取了眾多網(wǎng)絡(luò)框架的優(yōu)點恕齐,提升了穩(wěn)定性。
對于不會使用OkHttp的,推薦泡在網(wǎng)上的日子的一片博文OkHttp使用教程瞬逊。
OkHttp的調(diào)用可以簡單的分為以下幾步
- 生成一個OkHttpClient(用以總的控制)
- 用各種鍵值對包裝我們的Request
- 將請求加入隊列生成一個Call管理請求
- 若是同步請求直接執(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)點:
- 通過對線程進(jìn)行緩存妻献,減少了創(chuàng)建銷毀的時間損失
- 通過控制線程數(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ā)問題
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ì)是
此時判斷線程池內(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...這些都牽扯到緩存問題唯咬,大概對于緩存的判斷過程入下圖
okhttp內(nèi)部是通過轉(zhuǎn)化實現(xiàn)以上機(jī)制
參數(shù)解釋:
request:這些頭參數(shù)會被okhttp的流程裝置Interceptor打斷(下面會講),組裝成新的request
cacheCandidate:上次與服務(wù)器交互的緩存的Response,基于文件系統(tǒng)的Map畏浆,key為url的md5胆胰,置換算法為lru,即帶緩存Header的Response全度。
具體內(nèi)部實現(xiàn)為CacheStrategy煮剧,有興趣的可以自己看下源碼
接下來講講上面提到的流程工具Interceptor
從上圖看出主要用于兩個地方,對request進(jìn)行包裝,或者對response進(jìn)行解析勉盅,Interceptor可以用來監(jiān)控log佑颇,修改請求,修改結(jié)果,還有GZIP壓縮。
對于request加工過程
- 加工過程是一個自增遞歸調(diào)用過程旺入,直到將攔截器全部調(diào)用完潭陪。(header加一個map就生成一個攔截器)
- 處理完后,獲得網(wǎng)絡(luò)請求畔派,getResponse(),將按照http協(xié)議規(guī)范重新序列化對象中的數(shù)據(jù)信息,最終為Raw文本
獲得Response后的加工
總體是一個將Socket解析為Response對象的過程
- 讀取Raw解藻,反序列化為Statusline對象
- 以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)過考量直焙,測試得出的
- 首先因為服務(wù)器的帶寬是有限定的,他能提供的服務(wù)也是有限的砂轻,就像銀行辦事奔誓,可以開很多個窗口,但是銀行員工就那么幾個搔涝,你要是站著茅坑不拉屎厨喂,勢必影響整個銀行的辦事效率
- 服務(wù)器/防火墻一般都會有并發(fā)限制,畢竟這個就跟硬件搭界了
- 當(dāng)然不排除黑客用僵尸連接一直占著你服務(wù)器資源
這里對于連接的控制主要由Connection(連接主要對象)庄呈,StreamAllocation計數(shù)器(實現(xiàn)回收策略關(guān)鍵)蜕煌,ConnectionPool連接池以及Deque構(gòu)成
- 首先Connection是個接口,他的實現(xiàn)類是RealConnection诬留,這個對象保存了連接的信息斜纪,比如贫母,核心Socket對象,Handshake握手信息盒刚,Protocol信息...是整個請求的核心部分腺劣,也是管理單元
- 其次StreamAllocation是個計數(shù)器,用來記錄Connection被引用的次數(shù)因块,以便我們在清理整個ConnectionPool的時候可根據(jù)最少使用原則橘原,將引用置換或者清理
- Dequeue用來存放RealConnection
- 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篇文章