okhttp淺析


轉載自:http://www.ishenping.com/ArtInfo/69561.html

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(網絡攔截器)對請求依次處理,與服務的建立連接后,獲取返回數據,再經過上述攔截器依次處理后,最后將結果返回給調用方。
提供兩張圖便於理解和記憶:

 

 
6024478-fcfb26ca04a78fbc.png
okhttp整體流程1
 
6024478-80d45f4fcc34489e.png
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:負責讀取緩存以及更新緩存。
在請求階段:

  1. 讀取候選緩存cacheCandidate;
  2. 根據originOequest和cacheresponse創建緩存策略CacheStrategy;
  3. 根據緩存策略,來決定是否使用網絡或者使用緩存或者返回錯誤。
    具體的的緩存策略就是http的緩存策略,詳見下圖:
    在結果返回階段:
    負責將網絡結果進行緩存(使用於DiskLruCache)。
 
6024478-bcb83a4dccb3a2c4.png
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、連接池的清理

 
6024478-b2d3dfc5542b4cce.png
連接池清理1

 

在ConectionPool中有一個異步線程去清理連接池中的連接,首先通過cleanup方法執行清理,然后等待clean返回的時間后,再次進行清理,以此循環,持續清理。

 
6024478-da5bf326653b7671.png
連接池原理2

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

如何統計空閑連接呢?

 

 
6024478-076bec66f5dd894f.png
統計空閑連接

 

StreamAllocation創建一個Connection后會將自己添加到Connection的connection.allocations列表中,數據讀取完畢之后,會將自己從Connection的connection.allocations中移除,所以判讀一個Connection是否是空閑連接可以采用引用計數法,判斷connection.allocations列表中是否有StreamAllocation,如果沒有就是空閑連接,否則不是。

3、參考鏈接

因為本文是極度針對面試的,所以未解釋過多名詞和粘貼過多代碼,如果不明白其中原理,可以參考下面兩篇鏈接:
3.1、https://www.jianshu.com/p/6166d28983a2
3.2、https://juejin.im/post/5a704ed05188255a8817f4c9#heading-15


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM