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



這張圖只畫出了請求流程,沒有數據返回流程,後期會處理。

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)。

強制緩存:當客戶端第一次請求數據是,服務端返回了緩存的過期時間(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、連接池的清理


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

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、https://www.jianshu.com/p/6166d28983a2
3.2、https://juejin.im/post/5a704ed05188255a8817f4c9#heading-15

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章