日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OKHttp原理讲解之基本概念

發布時間:2023/12/31 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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原理讲解之基本概念的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。