OKHttp原理讲解之基本概念
前言:
1.1為什么要寫這篇文章?
OKHttp是square出品的開源通信框架,目前市面上大多數的APP都使用了該框架,那為什么這么多的APP都選擇使用該框架呢?有的人說別人都用我也就用唄,那肯定不是的。本文就帶你了解為什么我們要使用OKHttp,以及OKHttp的原理。
1.2 OKHttp項目地址:
https://github.com/square/okhttp
1.3 系列簡介:
OKHttp項目本身還是很大的,而且涉及到的知識點很多,所以一篇文章是很難概括全的,所以準備寫一個系列的文章來詳細講解OKHttp。
準備寫以下幾篇。
OKHttp原理講解之基本概念(預計03.15發布)
OKHttp原理講解之責任鏈模式及擴展(預計03.18發布)
OKHttp原理講解之重試攔截器(預計03.22發布)
OKHttp原理講解之路由攔截器
OKHttp原理講解之緩存攔截器
OKHttp原理講解之連接池攔截器
OKHttp原理講解之請求攔截器
PS:預計每周更新1-2篇的進度進行
1.4 OKHttp系列第一篇
本篇主要講解以下部分:
1.OKHttp的簡單使用
2.OKHttp的幾個主要概念
3.OKHttp發送請求的基本流程
4.調度器中線程的管理
5.為什么選擇使用OKHttp
一、OKHttp的幾個主要概念
PS:以下幾個中文名字都是按照我自己的理解取的,不是官方起名。
1.配置中心,OKHttpClient
OKHttp是一個可以高度定制化的框架,它提供了很多可配置的功能項,可以由開發者根據自主選擇去配置。比如Dns,Proxy,connectTimeout等等。
而OKHttpClient就是這些配置的一個容器,它使用構造者模式去創建,開發者可以根據自己的需求自主選擇。
2.請求體,RealCall
對于每一個請求,OKHttp都封裝了了一個RealCall來執行具體請求的流程。所以自然的,五層責任鏈也是由它來創建和調用的。
3.調度器,Dispatcher
調度器負責每個請求體的具體執行。所以自然的,調度器中包含了一個請求線程池,以及請求隊列,執行隊列,等待隊列等等。?
4.責任鏈,List<Interceptor>
OKHttp中默認包含五層責任鏈,分別為
RetryAndFollowUpInterceptor:重試跟進攔截器
BridgeInterceptor:橋接攔截器
CacheInterceptor:緩存攔截器
ConnectInterceptor:連接池攔截器
CallServerInterceptor:請求攔截器
一個請求的完整發送流程,會從上向下依次執行,如果某一個攔截器判斷執行完成,則一層一層向上返回最終結果。
所以五層攔截器也是OKHttp的核心,后面會有專門的篇章來一一進行講解。
5.請求,Request
顧名思義,包含請求所需要的所有信息。
6.響應,Response
顧名思義,包含響應所需要的所有信息。
二、OKHttp基本使用
這不是本文要講解的重點,所以只是簡單的介紹下使用方式。
2.1配置OKHttpClient
這里我只進行了一個配置,配置了一個緩存目錄。
val builder = OkHttpClient.Builder()builder.cache(Cache(File(context?.filesDir?.absolutePath + File.separator + "ok"), 100))val client = builder.build()2.2發送請求,
請求分兩種,同步請求和異步請求,同步請求需要在子線程執行。
第一種同步的方式:
val builder = Request.Builder()val request = builder.url("https://www.baidu.com").build()val newCall = client.newCall(request)service.execute {val response = newCall.execute()}第二種異步的方式:
val builder = Request.Builder()val request = builder.url("https://www.baidu.com").build()val newCall = client.newCall(request)newCall.enqueue(object : Callback {override fun onFailure(call: Call, e: IOException) {logI(call.toString());}override fun onResponse(call: Call, response: Response) {}})2.3 處理返回值
reponse中包含返回的數據流,我們只要序列化返回的data數組即可。
val content = IOHelper.readStrByCode(response.body()?.byteStream(), "utf-8")logI(content)readStrByCode方法鏈接,作用是把數組轉換為字符串。
https://github.com/aa5279aa/android_all_demo/blob/master/DemoClient/app/src/main/java/com/xt/client/util/IOHelper.java
三、OKHttp發送主體流程
異步和同步請求其實流程上差不多,只不過同步請求是直接返回reponse,而異步則是通過回調的方式通知。這里就以異步請求為例,講解整個請求的發送流程。
3.1?構建請求體RealCall
這里構建的RealCall中,主要包含原始的request,以及事件監聽。
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {// Safely publish the Call instance to the EventListener.RealCall call = new RealCall(client, originalRequest, forWebSocket);call.eventListener = client.eventListenerFactory().create(call);return call;}3.2?開始請求流程RealCall.enqueue()
這里主要做兩件事,
第一件事,對于觀察者進行回調
第二件事,交由調度器,在線程中執行AsyncCall的execute方法。
public void enqueue(Callback responseCallback) {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();eventListener.callStart(this);client.dispatcher().enqueue(new AsyncCall(responseCallback));}3.3?執行請求流程execute()
首先調用責任鏈,去獲取最終的響應。
然后進行通知callBack進行對應的回調。
最終通過調度器完整的結束該請求
protected void execute() {boolean signalledCallback = false;try {Response response = getResponseWithInterceptorChain();if (retryAndFollowUpInterceptor.isCanceled()) {signalledCallback = true;responseCallback.onFailure(RealCall.this, new IOException("Canceled"));} else {signalledCallback = true;responseCallback.onResponse(RealCall.this, response);}} catch (IOException e) {if (signalledCallback) {// Do not signal the callback twice!Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);} else {eventListener.callFailed(RealCall.this, e);responseCallback.onFailure(RealCall.this, e);}} finally {client.dispatcher().finished(this);}}}四、調度器中線程的管理
上面講了由調度器負責把一個一個的請求任務,分發給線程去管理。那么線程是如何管理的呢?
4.1 Dispatcher中的enqueue()方法
4.1.1 方法加鎖:
這個方法是加鎖的,避免多線程添加任務時,出現沖突。
4.1.2 判斷數量是否超標
這里判斷了正在執行的請求數量是否大于64,以及單個鏈接的請求數是否大于5(這里的64和5是可以配置的,單個鏈接指的是請求接口地址是一樣的),如果超過,則排隊;否則則可以立即執行。
synchronized void enqueue(AsyncCall call) {if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {runningAsyncCalls.add(call);executorService().execute(call);} else {readyAsyncCalls.add(call);}}4.1.3?獲取并創建線程池
1.判斷是否存在線程池
如果不存在,則創建線程池。否則直接返回
這里擴展一個小問題,為什么使用的時候才創建線程池呢?我猜測是為了性能,提早創建了線程池不用浪費性能。
2.創建線程池
這里使用的線程池配置參數,核心線程數為0,最大線程數為MAX_VALUE,非核心線程存活時間為60秒。
隊列使用的是SynchronousQueue,適合生產者消費者模型。具體SynchronousQueue可以自行百度,這里就不擴展了。
public synchronized ExecutorService executorService() {if (executorService == null) {executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));}return executorService;}3.為什么不會出現性能問題
我們這里看到,OKHttp請求線程池數量并沒有設置上線,那么不會存在性能問題嗎?實際上由于之前已經有了maxRequest的限制,所以線程數量并不會真的達到MAX_VALUE的級別。
所以可以這么理解OK的線程池策略,同時并發每一個請求,那么每一個請求都是一個單獨線程去處理。如果一個請求完成后,那么其所占用的線程就可以被釋放供后續請求使用。
4.自定義線程池策略
即使已經限制了最大請求數64,對于某些性能較差的手機,有可能真的大并發請求時還是吃力的。所以沒關系,OK的一大特色就是可配置性強。沒關系,線程池我們可以自定義傳入,讓OK使用我們自定義的線程池。
我們可以通過Dispatcher的有參構造函數傳入自定義線程池。
val newSingleThreadExecutor = Executors.newSingleThreadExecutor()val dispatcher = Dispatcher(newSingleThreadExecutor)val builder1 = OkHttpClient.Builder()builder1.dispatcher(dispatcher);val build = builder.build()4.1.4?線程池調度調用Call
1.執行AsyncCall
獲取到線程池后,就可以交給其最終的call任務,讓其去執行。最終會調用到AsyncCall
的execute方法。
executorService().execute(call);2.為什么會執行AsyncCall的execute方法
AsyncCall繼承自NameRunnable,NameRunnable繼承自Runnable。NameRunnable中的實現如下,所以會執行NameRunnable的execute方法。
public abstract class NamedRunnable implements Runnable {protected final String name;public NamedRunnable(String format, Object... args) {this.name = Util.format(format, args);}@Override public final void run() {String oldName = Thread.currentThread().getName();Thread.currentThread().setName(name);try {execute();} finally {Thread.currentThread().setName(oldName);}}protected abstract void execute(); }五、為什么選擇使用OKHttp
常規答案
這里面試經常被問到的一道題,我面試別人的時候也經常問。很常規的答案如下:
1.大家都在用,所以我也就用。
2.大廠出門的,穩定性好,比較放心。
3.使用簡單,用起來比較方便。
4.持續維護中,不擔心出了問題沒人解決
更為合適的答案
但是其實這題面試官更想考驗的是對OKHttp的了解程度,所以除了上面那些優勢,我們還可以從OKHttp框架的優勢的角度去談一談:
1.可擴展性高。類似于緩存,Dns,請求/連接/響應超時時間等等都可以通過配置傳入,甚至線程池都可以根據自己的需求來配置。
2.OKHttp使用了連接池緩存,提高通信效率。
3.責任鏈五層攔截器模式,每層功能清晰明了,并且提供了兩層可擴展的攔截器方便進行所需要的改造。
4.層次結構清晰,方便進行問題的排查。
5.觀察者模式的充分使用,查看請求狀態和監控請求狀態變得十分簡單。
6.使用了OKIO框架進行數據的處理,效率和安全性上更高。
等等,隨著文章的不斷詳細逐漸補充。
總結
以上是生活随笔為你收集整理的OKHttp原理讲解之基本概念的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASCLL码
- 下一篇: win10安装qt5.12保姆级教程(图