1. 程式人生 > >Okhttp3 總結研究 (面試)

Okhttp3 總結研究 (面試)

OKhttp3 是最近比較主流的網路請求框架。面試中,常會問道你對okhttp3是否有深入的瞭解。在這篇文章我總結了下okhttp3的原理(非用法,用法自己百度就行),以及大神們根據原始碼分析OKhttp3比較好的文章。

1.首先是大神們對OKhttp3的原始碼分析:(轉載)

Android OkHttp(三)原始碼解析:https://blog.csdn.net/zxw136511485/article/details/52872724 (這是我第一篇看的okhttp的原始碼解析,根據這篇文章和自己檢視okhtttp 原始碼,看okhttp內部,這篇文章大概講清楚okhttp 內部構造,雖然有些語言表達上的錯誤,這篇也是針對okhttp的.建議可以先看這篇,先了解大概)

okhttp原始碼解析:https://blog.csdn.net/json_it/article/details/78404010 (這是第二篇,也是最近在看的一篇,大神分析的比較全,也比較準確,詳細解析的okhttp3)

OkHttp3原始碼分析[綜述]:https://blog.csdn.net/json_it/article/details/78404010 (這篇是將原始碼分析,拆成了5個部分,分析也很很好,適合看)

Android okhttp3 建立Socket的底層實現追蹤 :https://blog.csdn.net/hello2mao/article/details/53159151 (這篇描述了OKhttp3 socket底層的實現)

OkHttp3原始碼和設計模式

https://www.imooc.com/article/24025?block_id=tuijian_wz (這篇從設計模式的角度分析OKhttp3)

OkHttp3對RealCall的註釋及上篇是Dispatcher

https://blog.csdn.net/dingding_android/article/details/51942000

2.根據所看的文章,及原始碼的檢視,自己做了簡略的總結

Okhttp3: 從以下方面總結

為什麼okHttp3 好用呢?

OkHttp是一個精巧的網路請求庫,有如下特性: 

1)支援http2,對一臺機器的所有請求共享同一個socket

2)內建連線池,支援連線複用,減少延遲

3)支援透明的gzip壓縮響應體

4)通過快取避免重複的請求

5)請求失敗時自動重試主機的其他ip,自動重定向

6)好用的API

實現網路請求方法:

OkHttp3的最底層是Socket,而不是URLConnection,它通過Platform的Class.forName()反射獲得當前Runtime使用的socket庫

socket發起網路請求的流程一般是:
(1). 建立socket物件;
(2). 連線到目標網路;

(3). 進行輸入輸出流操作。

(1)(2)的實現,封裝在connection介面中,具體的實現類是RealConnection。
(3)是通過stream介面來實現,根據不同的網路協議,有Http1xStream和Http2xStream兩個實現類
由於建立網路連線的時間較久(如果是HTTP的話,需要進行三次握手),而請求經常是頻繁的碎片化的,所以為了提高網路連線的效率,OKHttp3實現了網路連線複用

運用到的設計模式

單例模式:(建議用單例模式建立okHttpClient)OkHttpClient, 可以通過 new OkHttpClient() 或 new OkHttpClient.Builder() 來建立物件, 但是---特別注意, OkHttpClient() 物件最好是共享的, 建議使用單例模式建立。 因為每個 OkHttpClient 物件都管理自己獨有的執行緒池和連線池。 這一點很多同學,甚至在我經歷的團隊中就有人踩過坑, 每一個請求都建立一個 OkHttpClient 導致記憶體爆掉

外觀模式 : OKHttpClient 裡面組合了很多的類物件。其實是將OKHttp的很多功能模組,全部包裝進這個類中,讓這個類單獨提供對外的API,這種設計叫做外觀模式(外觀模式:隱藏系統的複雜性,並向客戶端提供了一個客戶端可以訪問系統的介面)

Builder模式 : OkHttpClient 比較複雜, 太多屬性, 而且客戶的組合需求多樣化, 所以OKhttp使用建造者模式(Build模式:使用多個簡單的物件一步一步構建成一個複雜的物件,一個 Builder 類會一步一步構造最終的物件)

工廠方法模式:Call介面提供了內部介面Factory(用於將物件的建立延遲到該工廠類的子類中進行,從而實現動態的
配置,工廠方法模式。(工廠方法模式:這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。在工廠模式中,我們在建立物件時不會對客戶端暴露建立邏輯,並且是通過使用一個共同的介面來指向新建立的物件。

享元模式:在Dispatcher的執行緒池中,所用到了享元模式,一個不限容量的執行緒池 , 執行緒空閒時存活時間為 60 秒。執行緒池實現了物件複用,降低執行緒建立開銷,從設計模式上來講,使用了享元模式。(享元模式:嘗試重用現有的同類物件,如果未找到匹配的物件,則建立新物件,主要用於減少建立物件的數量,以減少記憶體佔用和提高效能)

責任鏈模式:很明顯,在okhttp中的攔截器模組,執行過程用到。OkHttp3 的攔截器鏈中, 內建了5個預設的攔截器,分別用於重試、請求物件轉換、快取、連結、網路讀寫(責任鏈模式:為請求建立了一個接收者物件的鏈。這種模式給予請求的型別,對請求的傳送者和接收者進行解耦。這種型別的設計模式屬於行為型模式。在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個物件不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。)

策略模式 :CacheInterceptor 實現了資料的選擇策略, 來自網路還是來自本地? 這個場景也是比較契合策略模式場景, CacheInterceptor 需要一個策略提供者提供它一個策略(錦囊), CacheInterceptor 根據這個策略去選擇走網路資料還是本地快取。
快取的策略過程
1、 請求頭包含 "If-Modified-Since" 或 "If-None-Match" 暫時不走快取
2、 客戶端通過 cacheControl 指定了無快取,不走快取
3、客戶端通過 cacheControl 指定了快取,則看快取過期時間,符合要求走快取。

4、 如果走了網路請求,響應狀態碼為 304(只有客戶端請求頭包含 "If-Modified-Since" 或 "If-None-Match" ,伺服器資料沒變化的話會返回304狀態碼,不會返回響應內容), 表示客戶端繼續用快取。

(策略模式:一個類的行為或其演算法可以在執行時更改。這種型別的設計模式屬於行為型模式。策略模式中,我們建立表示各種策略的物件和一個行為隨著策略物件改變而改變的 context 物件。策略物件改變 context 物件的執行演算法。)

原始碼中用到的幾個重要的類及作用解釋:

1.OkhttpClient :對外的API,OKHttp的很多功能模組,全部包裝進這個類;建立分為兩種:一種是new  OkHttpClient()的方式,另一種是使用建造者(Builder)模式 -- new OkHttpClient.Builder()....Build()。那麼這兩種方式有什麼區別呢?

第一種:new OkHttpClient(),OkHttp做了很多工作,很多我們需要的引數在這裡都獲得預設值,也就是預設值設定。

第二種:預設的設定和第一種方式相同,但是我們可以利用建造者模式單獨的設定每一個屬性;注意事項:OkHttpClient強烈建議全域性單例使用,因為每一個OkHttpClient都有自己單獨的連線池和執行緒池,複用連線池和執行緒池能夠減少延遲、節省記憶體。

2.RealCall類:整合Call類,從原始碼中,可看到使用Call類,傳送出(同步/非同步)請求.RealCall的主要作用:傳送請求,當中還有攔截器的建立過程,非同步回撥。

3.Dispatcher類(排程器,多執行緒)儲存同步和非同步Call的地方,並負責執行非同步AsyncCall

4.攔截器鏈:有使用者自定義的interceptor、retryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、ConnectInterceptor、 networkInterceptors、CallServerInterceptor。依次通過以上攔截器,傳遞給RealCall中的ApplicationInterceptorChain。攔截器之所以可以依次呼叫,並最終再從後先前返回Response,都依賴於ApplicatiionInterceptorChain的proceed方法.

5.HttpEngine類:OKhttp底層的實現,(還在看)

快取策略:提到快取策略,就要提到CacheInterceptor攔截器,如下圖


CacheStrategy實現快取策略,CacheStrategy使用Factory模式進行構造,該類決定是使用快取還是使用網路請求

Cache是封裝了實際的快取操作;DiskLruCache:Cache基於DiskLruCache;

執行緒池(同步,非同步):


針對非同步請求,Dispatcher使用了兩個Deque,一個儲存準備執行的請求,一個儲存正在執行的請求,為什麼要用兩個呢?因為Dispatcher預設支援最大的併發請求是64個,單個Host最多執行5個併發請求,如果超過,則Call會先被放入到readyAsyncCall中,當出現空閒的執行緒時,再將readyAsyncCall中的執行緒移入到runningAsynCalls中,執行請求。

如果正在執行的請求總數<=64 && 單個Host正在執行的請求<=5,則將請求加入到runningAsyncCalls集合中,緊接著就是利用執行緒池執行該請求,否則就將該請求放入readyAsyncCalls集合中。

未完待續。