OkHttp 知名第三方網絡框架SDK,使用簡單,性能優秀,可是內核並不簡單,此係列文章,專挑硬核知識點詳細講解. 何爲硬核,就是要想深刻研究,你絕對繞不過去的知識點
OkHttp是時下很是流行的網絡編程框架,由行業巨佬 Square公司開源,不少其餘的流行框架好比 retrofit的底層也是okhttp,只不過使用了註解反射動態代理將其進行了封裝。流行版本爲:3.10.0,最新版本爲:4.0.1,只不過將實現語言從java改爲了kotlin。
相對於其餘網絡框架,有以下優勢:java
添加gradle依賴編程
dependencies { .... implementation ("com.squareup.okhttp3:okhttp:4.0.1") }
Java調用(同步請求,異步請求)設計模式
public class MyRequest { /** * 異步請求 */ public void sendReqAsync() { OkHttpClient client = new OkHttpClient.Builder().build(); Request request=newRequest.Builder().url("http://www.baidu.com").build(); Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(@NotNull Call call,@NotNull IOException e) { Log.d("sendReqTag", "onFailure:"+e.getLocalizedMessage()); } @Override public void onResponse(@NotNull Call call,@NotNull Response response) throws IOException { String s = new String().concat(response.code() +"\n") .concat(response.message()+"\n") .concat(response.body().string()); Log.d("sendReqTag","onSuccess\n "+ s); } }); } /** * 同步請求 */ public void sendReqSync() { OkHttpClient client = new OkHttpClient.Builder().build(); Request request = new Request.Builder().url("http://www.baidu.com").build(); Call call = client.newCall(request); try { Response response = call.execute(); String s = new String().concat(response.code() + "\n") .concat(response.message() + "\n") .concat(response.body().string()); Log.d("sendReqTag","onSuccess\n "+s); } catch (IOException e) { e.printStackTrace(); } } }
OkHttp的簡單使用方法大體使用如上,其中也有一些細節須要注意:緩存
一、在應用層使用OkHttp,必然會涉及到4個重要元素:性能優化
2.執行網絡請求必須在manifest中申請 INTERNET權限,否則會拋異常.網絡
三、完整的一個請求執行出去,流程以下圖:
架構
OkHttp源碼核心類之一:分發器詳解併發
上述,提到Call類,能夠選擇性執行 同步或者異步請求,可是不管同步異步,都必定會通過一個門戶:"分發器" : 索引進源碼(okhttp v3.10.0):
框架
雖然用戶不須要直接操做分發器,可是 分發器,做爲 OkHttp架構的一個門戶層,是全部請求的必經之路,其中的代碼仍是有必要了解細節的。異步
進入 分發器Dispatcher
以後, 會執行 getResponseWithInterceptorChain()
來執行這個 Call任務,獲得一個 Response
,其中的細節分爲兩步:
一、client.dispatcher().executed(this);
,進入源碼能夠看到 僅僅是執行了runningSyncCalls.add(call);
,將call對象加入到了一個雙端隊列Deque<RealCall>runningSyncCalls
中。
二、getResponseWithInterceptorChain()
是執行網絡請求的核心內容,涉及到攔截器,在這一節上暫時不詳述。
同步請求的執行步驟十分簡單,將任務加入到 runningSyncCalls列表,而且直接執行核心方法,同步阻塞拿到response。
異步請求進入分發器以後,
可能會被加入到 Deque<AsyncCall>runningAsyncCalls
這麼一個雙端隊列中,而後 executorService().execute(call);
其實是用了線程池來執行了這個異步任務。可是,請注意(仍是剛纔的enqueue方法代碼)這裏有一個判斷條件 if分支 :
這個條件是否知足,將會直接決定是直接執行這個任務,仍是將任務加入到 readyAsyncCalls 雙端隊列.
那麼設置這個條件的目的是什麼呢?從變量命名來看: runningAsyncCalls 執行中的異步任務 runningCallsForHost 同一個域名正在執行的任務數 readyAsyncCalls 預備執行的任務隊列(還沒有執行)
當正在執行的任務數小於最大值(默認爲64)而且,同一個域名正在請求的任務數小於最大值(默認5)時,纔會當即執行,不然,這個任務會被加入到 readyAsyncCalls中等待安排。
那麼問題來了,readyAsyncCalls中的任務何時會被執行?追蹤代碼:追蹤 readyAsyncCalls 的使用代碼,找到 遍歷這個隊列的地方:
繼續追蹤,找到了這個 finish方法:
繼續追蹤finish在哪裏調用的,找到兩處:
因此,得出結論:在一個任務(不管同步仍是異步)結束以後,分發器中的異步任務,存在兩個隊列,一個 running隊列,一個 ready隊列,當 running隊列的size小於最大值,而且同一個域名正在執行的任務數小於最大值時,能夠直接加入到running隊列,當即執行。若是不知足這條件,這個異步任務就會被加入到 ready隊列.
在任意一個任務( 不管同步或是異步任務)執行完畢( 不管成敗)以後,就會遍歷 ready隊列,每次從 ready隊列中取出一個任務,判斷同時執行的異步任務數是否達到上限,而且同一主機的訪問數是否達到上限,若是都知足,就加入到running隊列,而且當即執行,不知足,就中止遍歷。周而復始,直到全部的異步任務都執行完。
文字不夠形象,畫個圖表示。
關於okhttp的分發器Dispatcher用到的線程池
同步請求,沒有用到線程池。
可是異步請求的代碼中,有這麼一句。
咱們知道,爲何這裏會用到線程池呢?
1.觀察 同步或者異步的call的實例
那麼這個 Call是什麼?它是一個接口,它的惟一實現類是 RealCall,
在 RealCall
中,異步請求的執行方法, enqueue()
實際上是交給了 分發器一個 AsyncCall
對象,它繼承自 NamedRunnable
可命名的 Runnable
任務。因此,這裏能夠用 線程池 ExecutorService
來執行這個 Runnable
.
進一步觀察這個線程池的細節:
它是一個核心線程數爲0的線程池,而且使用了一個無容量的阻塞隊列做爲參數。其實也不沒必要本身去建立線程池,而能夠直接使用 Executors.newCachedThreadPool();
來建立,效果同樣。線程池,系統提供了有多種默認實現
爲何okhttp恰恰選擇了這一種?
爲了實現最大併發量。
詳解以下:既然這裏提到了線程池,那麼就把線程池的基本機制整理一下:
線程池的構造函數中,有一個阻塞隊列參數。
它有3個實現類:ArrayBlockingDeque/ LinkedBlockingDeque / SynchronousQueue
是咱們線程池常常用的。前面2個都是有容量的,而第三個是無容量的,加入進去,必定會失敗。而參照上面線程池的工做流程圖,若是加入失敗,就會嘗試去非核心線程執行任務。這樣,便保證了每個提交進來的異步任務,都會當即嘗試去執行,而不是塞入等待隊列中等待空閒線程,從而確保了 異步任務的併發。
OkHttp源碼核心類之一:攔截器簡述
上面講解分發器的時候,提到了 RealCall類的 getResponseWithInterceptorChain()
方法。它是一個網絡請求執行的真正核心方法。進入方法:
首先解釋一下 責任鏈模式,它是21種基本設計模式中,行爲模式中一種。下面的案例能夠很好地解釋它:
當一個國企要採購一批設備的時候,按照上圖整個任務流程中,存在5個對象,都能對採購流程形成影響,採購任務開始的時候,是從上到下依次對採購流程負責。而總經理,他纔不關心下面的人怎麼操做,他只關心最後的結果。正如此案例中所述,okhttp的責任鏈模式,使用者也不須要關心這個請求到底經歷了哪些過程,他只知道,我給了request,你就要給我response,而過程當中,發生做用的各種攔截器,無需使用者知道,這樣就達成了 面向對象程序開發中的 最少知道原則。
而,這些攔截器,偏偏是okhttp的核心內容,下篇文章將會詳細講解。
本文是 okhttp的開篇,若是要詳細解讀 okhttp的每一個細節,每一篇文章將會顯得 很是冗長並且乏味,因此我選了重要節點着重分析。就像攻城略地打天下,先 佔領據點,再 企圖擴張,一步一個腳印,穩紮穩打,才能長遠發展
問題加羣討論哦 本專欄爲那些想要進階成爲高級Android工程師所準備。 從初中級轉向高級工程師須要從技術的廣度,深度上都有必定的造詣。因此本專欄就主要爲你們分享一些技術,技術原理等。 包含源碼解析,自定義View,動畫實現,架構分享等。 內容難度適中,篇幅精煉,天天只需花上十幾分鍾閱讀便可。 你們能夠跟我一塊兒探討,歡迎加羣探討,有flutter—底層開發-性能優化—移動架構—資深UI工程師 —NDK-人工智能相關專業人員和視頻教學資料 。後續還有最新鴻蒙系統相關內容分享。 QQ羣號:765080964