關注我,一個仍存夢想的屌絲程序員,每天爲你分享高質量編程博客。
follow us for dream
前言
手把手講解系列文章,是我寫給各位看官,也是寫給我自己的。
文章可能過分詳細,但是這是爲了幫助到儘量多的人,畢竟工作5,6年,不能老吸血,也到了回饋開源的時候.
這個系列的文章:
1、用通俗易懂的講解方式,講解一門技術的實用價值
2、詳細書寫源碼的追蹤,源碼截圖,繪製類的結構圖,儘量詳細地解釋原理的探索過程
3、提供Github 的 可運行的Demo工程,但是我所提供代碼,更多是提供思路,拋磚引玉,請酌情cv
4、集合整理原理探索過程中的一些坑,或者demo的運行過程中的注意事項
5、用gif圖,最直觀地展示demo運行效果如果覺得細節太細,直接跳過看結論即可。
本人能力有限,如若發現描述不當之處,歡迎留言批評指正。
學到老活到老,路漫漫其修遠兮。與衆君共勉 !
引子
OkHttp 知名第三方網絡框架SDK,使用簡單,性能優秀,但是內核並不簡單,此係列文章,專挑硬核知識點詳細講解.
何爲硬核,就是要想深入研究,你絕對繞不過去的知識點
正文大綱
OkHttp是什麼
OkHttp怎麼用
OkHttp源碼核心類之一:分發器詳解
OkHttp源碼核心類之一:攔截器簡述
正文
OkHttp是什麼
OkHttp是時下非常流行的網絡編程框架,由行業巨佬
Square
公司開源,很多其他的流行框架比如retrofit
的底層也是okhttp,只不過使用了註解反射動態代理將其進行了封裝。
流行版本爲:3.10.0,最新版本爲:4.0.1,只不過將實現語言從java改成了kotlin。
相對於其他網絡框架,有如下優點:
支持
Spdy
、Http1.X
、Http2
、Quic
以及WebSocket
連接池複用底層
TCP(Socket)
,減少請求延時無縫的支持
GZIP
減少數據流量緩存響應數據減少重複的網絡請求
請求失敗自動重試主機的其他
ip
,自動重定向
OkHttp怎麼用
添加gradle依賴
dependencies {
public class MyRequest {
OkHttp的簡單使用方法大致使用如上,其中也有一些細節需要注意:
1、在應用層使用OkHttp,必然會涉及到4個重要元素:
OkHttpClient類(產生OkHttp客戶端實例)
Request類(請求封裝)
Call類(網絡任務封裝,並決定是要
同步執行
還是異步執行
,注意,同步請求不
可以放在主線程
中,但是異步請求可以 )Response類(網絡任務執行之後的回調)
2、執行網絡請求必須在manifest中申請
INTERNET
權限,不然會拋異常.
3、完整的一個請求執行出去,流程如下圖:
image.png
OkHttp源碼核心類之一:分發器詳解
上述,提到Call類,可以選擇性執行 同步或者異步請求,但是無論同步異步,都一定會經過一個門戶:“分發器” :
索引進源碼(okhttp v3.10.0):
異步請求.png
同步請求.png
雖然用戶不需要直接操作分發器,但是 分發器,作爲OkHttp
架構的一個門戶層,是所有請求的必經之路,其中的代碼還是有必要了解細節的。
同步請求
進入分發器 Dispatcher
之後, 會執行 getResponseWithInterceptorChain()
來執行這個Call
任務,得到一個Response
,其中的細節分爲兩步:
1、
client.dispatcher().executed(this);
,進入源碼可以看到 僅僅是執行了runningSyncCalls.add(call);
,將call對象加入到了一個雙端隊列Deque<RealCall> runningSyncCalls
中。
2、getResponseWithInterceptorChain()
是執行網絡請求的核心內容,涉及到攔截器,在這一節上暫時不詳述。
同步請求的執行步驟十分簡單,將任務加入到 runningSyncCalls列表,並且直接執行核心方法,同步阻塞拿到response。
異步請求
異步請求進入分發器之後,
image.png
可能會被加入到
Deque<AsyncCall> runningAsyncCalls
這麼一個雙端隊列中,然後executorService().execute(call);
實際上是用了線程池來執行了這個異步任務。
但是,請注意(還是剛纔的enqueue方法代碼)這裏有一個判斷條件 if分支 :image.png
這個條件是否滿足,將會直接決定是直接執行這個任務,還是將任務加入到 readyAsyncCalls 雙端隊列.
那麼設置這個條件的目的是什麼呢?從變量命名來看:
runningAsyncCalls 執行中的異步任務
runningCallsForHost 同一個域名正在執行的任務數
readyAsyncCalls 預備執行的任務隊列(尚未執行)
當正在執行的任務數小於最大值(默認爲64)並且,同一個域名正在請求的任務數小於最大值(默認5)時,纔會立即執行,否則,這個任務會被加入到 readyAsyncCalls中等待安排。
那麼問題來了,readyAsyncCalls中的任務什麼時候會被執行?
追蹤代碼:追蹤 readyAsyncCalls 的使用代碼,找到遍歷
這個隊列的地方:
image.png
繼續追蹤,找到了這個 finish方法:
image.png
繼續追蹤finish在哪裏調用的,找到兩處:
image.png
image.png
所以,得出結論:
在一個任務(無論同步還是異步)結束之後,分發器中的異步任務,存在兩個隊列,一個running隊列
,一個ready隊列
,當running隊列
的size小於最大值,並且同一個域名正在執行的任務數小於最大值時,可以直接加入到running隊列,立即執行。如果不滿足這條件,這個異步任務就會被加入到 ready隊列.在任意一個任務(
無論同步或是異步任務
)執行完畢(無論成敗
)之後,就會遍歷ready隊列
,每次從ready隊列
中取出一個任務,判斷同時執行的異步任務數是否達到上限,並且同一主機的訪問數是否達到上限,如果都滿足,就加入到running隊列,並且立即執行,不滿足,就停止遍歷。週而復始,直到所有的異步任務都執行完。
文字不夠形象,畫個圖表示。
未命名文件.jpg
關於okhttp的分發器Dispatcher用到的線程池
同步請求,沒有用到線程池。
image.png
但是異步請求的代碼中,有這麼一句。
image.png
我們知道,爲什麼這裏會用到線程池呢?
1.觀察 同步或者異步的call的實例。
image.png
那麼這個
Call
是什麼?它是一個接口,它的唯一實現類是RealCall
,image.png
在
RealCall
中,異步請求的執行方法,enqueue()
其實是交給了 分發器一個AsyncCall
對象,它繼承自NamedRunnable
可命名的Runnable
任務。所以,這裏可以用 線程池ExecutorService
來執行這個Runnable
.
進一步觀察這個線程池的細節:
image.png
它是一個核心線程數爲0的線程池,並且使用了一個無容量的阻塞隊列作爲參數。
其實也不不必自己去創建線程池,而可以直接使用Executors.newCachedThreadPool();
來創建,效果一樣。
線程池,系統提供了有多種默認實現image.png
爲什麼okhttp偏偏選擇了這一種?
答:爲了實現最大併發量。
詳解如下:
既然這裏提到了線程池,那麼就把線程池的基本機制整理一下:
線程池的工作流程圖.jpg
線程池的構造函數中,有一個阻塞隊列參數。
image.png
它有3個實現類:
ArrayBlockingDeque
/LinkedBlockingDeque
/SynchronousQueue
是我們線程池經常用的。
前面2個都是有容量的,而第三個是無容量的,加入進去,一定會失敗。而參照上面線程池的工作流程圖,如果加入失敗,就會嘗試去非核心線程執行任務。這樣,便保證了每一個提交進來的異步任務,都會立即嘗試去執行,而不是塞入等待隊列中等待空閒線程,從而確保了 異步任務的併發。
OkHttp源碼核心類之一:攔截器簡述
上面講解分發器的時候,提到了 RealCall類的getResponseWithInterceptorChain()
方法。它是一個網絡請求執行的真正核心方法。
進入方法:
核心方法.png
新建一個攔截器List,並且放入各種攔截器對象
將攔截器list,交給RealInterceptorChain,進行責任鏈模式的調用,最終得出Response.
首先解釋一下責任鏈模式
,它是21種基本設計模式中,行爲模式中一種。下面的案例可以很好地解釋它:
責任鏈模式案例
當一個國企要採購一批設備的時候,按照上圖整個任務流程中,存在5個對象,都能對採購流程造成影響,採購任務開始的時候,是從上到下依次對採購流程負責。而總經理,他纔不關心下面的人怎麼操作,他只關心最後的結果。
正如此案例中所述,okhttp的責任鏈模式,使用者也不需要關心這個請求到底經歷了哪些過程,他只知道,我給了request,你就要給我response,而過程中,發生作用的各類攔截器,無需使用者知道,這樣就達成了面向對象程序開發
中的最少知道原則
。
而,這些攔截器,恰恰是okhttp的核心內容,下篇文章將會詳細講解。
結語
本文是okhttp
的開篇,如果要詳細解讀okhttp
的每個細節,每一篇文章將會顯得非常冗長而且乏味
,所以我選了重要節點着重分析。就像攻城略地打天下,先佔領據點
,再企圖擴張
,一步一個腳印,穩紮穩打,才能長遠發展
回覆關鍵字:
1、回覆 “10” 查看 最有價值的10個spring boot開源項目
2、回覆 “國旗” 獲取國旗頭像教程
3、回覆 “Ubuntu” 獲取100 個最佳 Ubuntu 應用 和 linux神器
4、回覆 “ idea ” 獲取**最新idea破解教程 和 裝逼神奇
5、回覆 “ ssh ” 獲取史上最好的 ssh工具 支持mac
6、回覆 “ 代金券 ” 騰訊雲和阿里雲代金券
7、回覆 “免費” 可以獲取免費的java面試資源和學習視頻
推薦閱讀:
面試官問我:一個 TCP 連接可以發多少個 HTTP 請求?我竟然回答不上來…
免責聲明:
1.本公衆號所轉載文章均來自公開網絡。
2.如果出處標註有誤或侵犯到原著作者權益,請聯繫刪除。
3.轉載本公衆號中的文章請註明原文鏈接和作者,否則產生的任何版權糾紛均與本公衆號無關。