Android中okhttp原理詳解-極度針對面試篇
後期我會面試50家二線以上的網際網路公司,不斷總結完善和思考,寫出一本完美的安卓面試書籍,大家支援我嗎?
目錄
1、okhttp工作的大致流程
2、okhttp中的連線池
3、參考連線
1、okhttp工作的大致流程
1.1、整體流程
(1)、當我們通過OkhttpClient建立一個Call,併發起同步或非同步請求時;
(2)、okhttp會通過Dispatcher對我們所有的RealCall(Call的具體實現類)進行統一管理,並通過execute()及enqueue()方法對同步或非同步請求進行處理;
(3)、execute()及enqueue()這兩個方法會最終呼叫RealCall中的getResponseWithInterceptorChain()方法,從攔截器鏈中獲取返回結果;
(4)、攔截器鏈中,依次通過RetryAndFollowUpInterceptor(重定向攔截器)、BridgeInterceptor(橋接攔截器)、CacheInterceptor(快取攔截器)、ConnectInterceptor(連線攔截器)、CallServerInterceptor(網路攔截器)對請求依次處理,與服務的建立連線後,獲取返回資料,再經過上述攔截器依次處理後,最後將結果返回給呼叫方。
提供兩張圖便於理解和記憶:

okhttp整體流程1

okhttp整體流程2
這張圖只畫出了請求流程,沒有資料返回流程,後期會處理。
1.2、各大攔截器的原理解析
1.2.1、RetryAndFollowUpInterceptor:負責重定向
構建一個StreamAllocation物件,然後呼叫下一個攔截器獲取結果,從返回結果中獲取重定向的request,如果重定向的request不為空的話,並且不超過重定向最大次數的話就進行重定向,否則返回結果。注意:這裡是通過一個while(true)的迴圈完成下一輪的重定向請求。
(1)、StreamAllocation為什麼在第一個攔截器中就進行建立?
便於取消請求以及出錯釋放資源。
(2)、StreamAllocation的作用是什麼?
StreamAllocation負責統籌管理Connection、Stream、Call三個實體類,具體就是為一個Call(Realcall),尋找( findConnection() )一個Connection(RealConnection),獲取一個Stream(HttpCode)。
1.2.2、BridgeInterceptor
負責將原始Requset轉換給傳送給服務端的Request以及將Response轉化成對呼叫方友好的Response,具體就是對request新增Content-Type、Content-Length、Connection、Accept-Encoding等請求頭以及對返回結果進行解壓等。
1.2.3、CacheInterceptor
CacheInterceptor:負責讀取快取以及更新快取。
在請求階段:
- 讀取候選快取cacheCandidate;
- 根據originOequest和cacheresponse建立快取策略CacheStrategy;
- 根據快取策略,來決定是否使用網路或者使用快取或者返回錯誤。
具體的的快取策略就是http的快取策略,詳見下圖:
在結果返回階段:
負責將網路結果進行快取(使用於DiskLruCache)。

okhttp&http快取策略
強制快取:當客戶端第一次請求資料是,服務端返回了快取的過期時間(Expires與Cache-Control),沒有過期就可以繼續使用快取,否則則不適用,無需再向服務端詢問。
對比快取:當客戶端第一次請求資料時,服務端會將快取標識(Etag/If-None-Match與Last-Modified/If-Modified-Since)與資料一起返回給客戶端,客戶端將兩者都備份到快取中 ,再次請求資料時,客戶端將上次備份的快取
標識傳送給服務端,服務端根據快取標識進行判斷,如果返回304,則表示快取可用,如果返回200,標識快取不可用,使用最新返回的資料。
ETag是用資源標識碼標識資源是否被修改,Last-Modified是用時間戳標識資源是否被修改。ETag優先順序高於Last-Modified。
1.2.4、ConnectInterceptor:負責與伺服器建立連線
使用StreamAllocation.newStream來和服務端建立連線,並返回輸入輸出流(HttpCodec),實際上是通過StreamAllocation中的findConnection尋找一個可用的Connection,然後呼叫Connection的connect方法,使用socket與服務端建立連線。
1.2.5、CallServerInterceptor:負責從伺服器讀取響應的資料
主要的工作就是把請求的Request寫入到服務端,然後從服務端讀取Response。
(1)、寫入請求頭
(2)、寫入請求體
(3)、讀取響應頭
(4)、讀取響應體
2、連線池原理
由於HTTP是基於TCP,TCP連線時需要經過三次握手,為了加快網路訪問速度,我們可以Reuqst的header中將Connection設定為keepalive來複用連線。
Okhttp支援5個併發KeepAlive,預設鏈路生命為5分鐘(鏈路空閒後,保持存活的時間),連線池有ConectionPool實現,對連線進行回收和管理。
2.1、連線池的清理

連線池清理1
在ConectionPool中有一個非同步執行緒去清理連線池中的連線,首先通過cleanup方法執行清理,然後等待clean返回的時間後,再次進行清理,以此迴圈,持續清理。

連線池原理2
1、首先統計空閒連線數量;
2、然後通過for迴圈查詢最長空閒時間的連線以及對應空閒時長;
3、然後判斷這個最長空閒時間的連線是否超出最大空閒連線數或者或者超過最大空閒時間,滿足其一則清除最長空閒的連線。如果不滿足清理條件,則返回一個對應等待時間。
這個對應等待的時間又分二種情況:
1 有空閒連線:則返回:keepAliveDurationNs-longestIdleDurationNs;
2 沒有空閒的連線,則返回:keepAliveDurationNs
注意:清除一個空閒連線後,會返回0,再次立即開始清理。
如何統計空閒連線呢?

統計空閒連線
StreamAllocation建立一個Connection後會將自己新增到Connection的connection.allocations列表中,資料讀取完畢之後,會將自己從Connection的connection.allocations中移除,所以判讀一個Connection是否是空閒連線可以採用引用計數法,判斷connection.allocations列表中是否有StreamAllocation,如果沒有就是空閒連線,否則不是。
3、參考連結
因為本文是極度針對面試的,所以未解釋過多名詞和貼上過多程式碼,如果不明白其中原理,可以參考下面兩篇連結:
3.1、 ofollow,noindex">https://www.jianshu.com/p/6166d28983a2
3.2、 https://juejin.im/post/5a704ed05188255a8817f4c9#heading-15