OkHttp原理分析小結(jié)
Okhttp 介紹
OkHttp 是 Square 公司開源的一款網(wǎng)絡(luò)框架,封裝了一個(gè)高性能的 http 請求庫。
https://github.com/square/okhttp
特點(diǎn)
- 支持 spdy、http2.0、websocket 等協(xié)議
- 支持同步、異步請求
- 封裝了線程池,封裝了數(shù)據(jù)轉(zhuǎn)換,提高性能。
- 在 Android 6.0 中自帶的網(wǎng)絡(luò)請求 API 的底層就是使用了
okhttp來進(jìn)行的 - 使用
okhttp比較接近真正的 HTTP 協(xié)議的框架
這個(gè)類主要是用來配置 okhttp 這個(gè)框架的,通俗一點(diǎn)講就是這個(gè)類是管理這個(gè)框架的各種設(shè)置的。
Call 類的工廠,通過 OkHttpClient 才能得到 Call 對象。
Okhttp 中幾個(gè)重要類的介紹
OkHttpClient
這個(gè)類主要是用來配置 okhttp 這個(gè)框架的,通俗一點(diǎn)講就是這個(gè)類是管理這個(gè)框架的各種設(shè)置的。
Call 類的工廠,通過 OkHttpClient 才能得到 Call 對象。
OkHttpClient使用注意
OkHttpClient 應(yīng)該被共享,使用 okhttp 這個(gè)框架的時(shí)候,最好要將 OkHttpClient 設(shè)置成單例模式,所有的 HTTP 在進(jìn)行請求的時(shí)候都要使用這一個(gè) Client 。因?yàn)槊總€(gè) OkHttpClient 都對應(yīng)了自己的連接池和線程池。減少使用連接池和線程池可以減少延遲和內(nèi)存的使用。相反的如果每個(gè)請求都創(chuàng)建一個(gè) OkHttpClient 的話會(huì)很浪費(fèi)內(nèi)存資源。
OkHttpClient的創(chuàng)建
OkHttpClient 有三個(gè)創(chuàng)建方法
第一個(gè)方法:直接使用 new OkHttpClient() 來創(chuàng)建一個(gè)實(shí)例對象就可以了,這個(gè)實(shí)例對象有默認(rèn)的配置。默認(rèn)請求連接超時(shí)時(shí)間 10 s ,讀寫超時(shí)時(shí)間 10 s,連接不成功會(huì)自動(dòng)再次連接。
第二個(gè)方法:就是通過 Builder的方式來自己定義一個(gè) OkHttpclient 。當(dāng)然如果你直接 build 沒有自己配置參數(shù)的話,效果和第一個(gè)方法是一樣的。
public final OkHttpClient = new OkHttpClient.Builder() .addInterceptor(new HttpLoggingInterceptor()) .cache(new Cache(cacheDir,cacheSize)) .等等配置 .build();
第三個(gè)方法:就是通過已有的 OkHttpClient 對象來復(fù)制一份共享線程池和其他資源的 OkHttpClient 對象。
OkHttpClient agerClient = client.newBuilder() .readTimeout(500,TimeUnit.MILLSECONS) .build();
這種方法的好處就是,當(dāng)我們有一個(gè)特殊的請求,有的配置有點(diǎn)不一樣,比如要求連接超過 1 s 就算超時(shí),這個(gè)時(shí)候我們就可以使用這個(gè)方法來生成一個(gè)新的實(shí)例對象,不過他們共用很多其他的資源,不會(huì)對資源造成浪費(fèi)。
關(guān)于 OkHttpClient 的配置改變都在 Builder 中進(jìn)行
不需要了可以關(guān)閉
其實(shí)持有的線程池和連接池將會(huì)被自定釋放如果他們保持閑置的話。
你也可以自動(dòng)釋放,釋放后將來再調(diào)用 call 的時(shí)候會(huì)被拒接。
client.dispatcher().excurorService().shutdown()
清除連接池,注意清除后,連接池的守護(hù)線程可能會(huì)立刻退出。
client.connectionPool().evictAll()
如果 Client 有緩存,可以關(guān)閉。注意:再次調(diào)用一個(gè)被關(guān)閉的 cache 會(huì)發(fā)生錯(cuò)誤。也會(huì)造成 crash。
client.cache().close();
OkHttp 在 HTTP/2 連接的時(shí)候也會(huì)使用守護(hù)線程。他們閑置的時(shí)候?qū)⒆詣?dòng)退出。
知道有這么一回事就行,一般不會(huì)主動(dòng)調(diào)用。
Call 類
Call 這個(gè)類就是用來發(fā)送 HTTP 請求和讀取 HTTP 響應(yīng)的一個(gè)類

這個(gè)類的方法很少,從上到下依次是:放棄請求、異步執(zhí)行請求、同步執(zhí)行請求。
Request 類
這個(gè)類就是相當(dāng)于 http 請求中的請求報(bào)文,是用來表達(dá)請求報(bào)文的,所以這里可以設(shè)置請求的 url、請求頭、請求體等等和請求報(bào)文有關(guān)的內(nèi)容。
主要方法羅列:
// 獲取請求 url
public HttpUrl url();
// 獲取請求方法類型
public String method();
// 獲取請求頭
public Headers headers();
//獲取請求體
public RequestBody body();
// 獲取 tag
public Object tag();
// 返回緩存控制指令,永遠(yuǎn)不會(huì)是 null ,即使響應(yīng)不包含 Cache-Control 響應(yīng)頭
public CacheControl cacheControl();
// 是否是 https 請求
public boolean isHttps();
// Resquest{method=" ",url=" ",tag = " "}
public String toString();
這是它的 Builder 中提供的方法,只設(shè)置 .url() 的時(shí)候默認(rèn)是 post 請求。
RequestBody
介紹完請求報(bào)文就要介紹請求體了,這都是和 http協(xié)議緊密聯(lián)系的。
RequestBody 就是用來設(shè)置請求體的,它的主要方法就是下面這個(gè)幾個(gè)靜態(tài)方法,用來生成對應(yīng)的請求體:

就是通過這幾個(gè)方法來產(chǎn)生對應(yīng)的不同的請求體。MediaType 是用來描述請求體或者響應(yīng)體類型的。比如請求體類型是 json 串格式的,那對應(yīng)的 MediaType 就是MediaType.parse("application/json; charset=utf-8"); ,如果上傳的是文件那么對應(yīng)的就是 application/octet-stream,還有幾個(gè)常用的類型 text/plain imge/png text/x-markdown 等等。
它還有兩個(gè)子類:

FormBody 這個(gè)請求體是我們平時(shí)最常用的,就是我們平時(shí)使用 post 請求的時(shí)候,參數(shù)是鍵值對的形式。就是使用這個(gè)請求體最簡單了。
說深一點(diǎn),對應(yīng)的請求報(bào)文是:
POST /test HTTP/1.1 請求行
Host: 32.106.24.148:8080 下面都是請求頭
Content-Type: application/x-www-form-urlencoded 用于指明請求體的類型。
User-Agent: PostmanRuntime/7.15.0
Accept: */*
Cache-Control: no-cache
Postman-Token: 954bda0d-dbc2-4193-addf-a7631cab2cfa,5ba2ebed-90b4-4f35-bcf5-80c4777de471
Host: 39.106.24.148:8080
accept-encoding: gzip, deflate
content-length: 133
Connection: keep-alive
cache-control: no-cachekey0=value0&key1=value1 請求體(也是我們的參數(shù))
這是發(fā)送的原始的報(bào)文格式,用代碼實(shí)現(xiàn)的話就是
// 創(chuàng)建客戶端
OkHttpClient client = new OkHttpclient();
// 建立請求體
FormBody formBody = new FormBody.Builder()
.add("key0", "value0")
.add("key1","value1")
.build();
// 建立請求報(bào)文
Request request = new Request.Builder
.post(formBody)
.url("請求url")
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addHeader("User-Agent", "PostmanRuntime/7.15.0")
.addHeader("Accept", "*/*")
.addHeader("Cache-Control", "no-cache")
.addHeader("Postman-Token", "954bda0d-dbc2-4193-addf-a7631cab2cfa,af7c027c-a7ba-4560-98ae-3a2a473ab88a")
.addHeader("Host", "39.106.24.148:8080")
.addHeader("accept-encoding", "gzip, deflate")
.addHeader("content-length", "133")
.addHeader("Connection", "keep-alive")
.addHeader("cache-control", "no-cache")
.build();
// 發(fā)起請求
client.newCall(request).excute();上面是使用了 FormBody 的形式,如果使用 RequestBody 的話就要更麻煩一些。
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "key0=value0&key1=value1");
Request request = new Request.Builder()
.url("http://39.106.24.148:8080/test")
.post(body)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addHeader("User-Agent", "PostmanRuntime/7.15.0")
.addHeader("Accept", "*/*")
.addHeader("Cache-Control", "no-cache")
.addHeader("Postman-Token", "954bda0d-dbc2-4193-addf-a7631cab2cfa,af7c027c-a7ba-4560-98ae-3a2a473ab88a")
.addHeader("Host", "39.106.24.148:8080")
.addHeader("accept-encoding", "gzip, deflate")
.addHeader("content-length", "133")
.addHeader("Connection", "keep-alive")
.addHeader("cache-control", "no-cache")
.build();
Response response = client.newCall(request).execute();當(dāng)然平時(shí)我們使用的時(shí)候,不用拼上這么多的請求頭,我這樣寫的目的就是為了更加還原請求報(bào)文。
還有一個(gè)子類 MultipartBody這個(gè)可以用來構(gòu)建比較復(fù)雜的請求體。
1995 年 Content-Type 的類型擴(kuò)充了 multipart/form-data 用來支持向服務(wù)器發(fā)送二進(jìn)制數(shù)據(jù)。如果一次提交多種類型的數(shù)據(jù),比如:一張圖片和一個(gè)文字,這個(gè)時(shí)候引入了 boundary ,boundary使得 POST 可以滿足這種提交多種不同的數(shù)據(jù)類型。通過 boundary 可以實(shí)現(xiàn)多個(gè)不同類型的數(shù)據(jù)同時(shí)存在在一個(gè) Request 中。兩個(gè) boundary之間就是一個(gè)類型的數(shù)據(jù),并且可以重新設(shè)置 Content-Type
與 HTML 文件上傳形式兼容。每塊請求體都是一個(gè)請求體,可以定義自己的請求頭。這些請求頭可以用來描述這塊請求。例如,他們的 Content-Disposition。如果 Content-Length 和 Content-Type 可用的話,他們會(huì)被自動(dòng)添加到請求頭中。
來看一下這種類型的請求報(bào)文是什么樣的:
POST /web/UploadServlet HTTP/1.1
Content-Type: multipart/form-data; boundary=e1b05ca4-fc4e-4944-837d-cc32c43c853a
Content-Length: 66089
Host: localhost.tt.com:8080
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/3.5.0–e1b05ca4-fc4e-4944-837d-cc32c43c853a
Content-Disposition: form-data; name=”file”; filename=”**.png”
Content-Type: image/png
Content-Length: 65744fdPNG
IHDR?0B7M?iM?M?CCPIM?CC ProfileH??……………………IEND?B`?
–e1b05ca4-fc4e-4944-837d-cc32c43c853a
Content-Disposition: form-data; name=”comment”
Content-Length: 30上傳一個(gè)圖
–e1b05ca4-fc4e-4944-837d-cc32c43c853a–
第一個(gè)數(shù)據(jù)是一張 png 的圖,重新設(shè)置了 Content-Type:image/png 中間的亂碼就是圖片的數(shù)據(jù)。這一堆數(shù)據(jù)前有一個(gè)空行,表示上下分別是請求頭、請求體。
第二個(gè)數(shù)據(jù),就是一個(gè)文本數(shù)據(jù)。
這樣它們一起構(gòu)成了請求體。
講起來可能比較復(fù)雜,就記住,當(dāng)既需要上傳參數(shù),又需要上傳文件的時(shí)候用這種請求體。
MediaType mediaType = MediaType.parse("image/png");
RequestBody requestBody = new MultipartBody.Builder()
// 需要設(shè)置成表單形式否則無法上傳鍵值對參數(shù)
.setType(MultipartBody.FORM)
.addPart(Headers.of("Content-Disposition", "form-data;name=\"title\""),
RequestBody.create(null, "Square Logo"))
.addPart(
Headers.of("Content-Disposition", "form-data;name=\"imge\""),
RequestBody.create(mediaType, new File("路徑/logo.png"))
).
build();
Request request = new Request.Builder()
.post(requestBody)
.url("https://api.imgur.com/3/image")
.build();
try {
mOkHttpClient.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}簡化寫法:
MediaType mediaType = MediaType.parse("image/png");
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title","logo")
.addFormDataPart("img","logo.png",RequestBody.create(mediaType,new File("路徑/logo.png")))
.build();Content-Disposition 可以用在消息體的子部分中,用來給出其對應(yīng)字段的相關(guān)信息。作為 multipart body 中的消息頭,第一個(gè)參數(shù)總是固定不變的 form-data; 附加的參數(shù)不區(qū)分大小寫,并且擁有參數(shù)值,參數(shù)名與參數(shù)值用等號(hào)連接,參數(shù)之間用分號(hào)分隔。參數(shù)值用雙引號(hào)括起來
// 比如這樣,就是這種固定的格式 "Content-Disposition","form-data;name=\"mFile\";filename=\"xxx.mp4\""
到這里關(guān)于請求的幾個(gè)重要的類就講完了。
總結(jié)一下
只要掌握 http 請求的原理,使用起 okhttp 來也就不是什么問題了。
首先 OkHttpClient 是用來設(shè)置關(guān)于請求工具的一些參數(shù)的,比如超時(shí)時(shí)間、是否緩存等等。
Call 對象是發(fā)起 Http 請求的對象,通過 Call 對象來發(fā)起請求。
發(fā)起請求的時(shí)候,需要有請求報(bào)文,Request 對象就是對應(yīng)的請求報(bào)文,可以添加對應(yīng)的請求行、請求頭、請求體。
說起請求體就是對應(yīng)了 RequestBody 了。然后這個(gè)網(wǎng)絡(luò)請求過程就完成了!
OKHTTP架構(gòu)圖

OKHttp發(fā)送主體流程

在使用OkHttp發(fā)起一次請求時(shí),對于使用者最少存在OkHttpClient、Request與Call三個(gè)角色。其中OkHttpClient和Request的創(chuàng)建可以使用它為我們提供的Builder(建造者模式)。而Call則是把Request交給OkHttpClient之后返回的一個(gè)已準(zhǔn)備好執(zhí)行的請求。
同時(shí)OkHttp在設(shè)計(jì)時(shí)采用的門面模式,將整個(gè)系統(tǒng)的復(fù)雜性給隱藏起來,將子系統(tǒng)接口通過一個(gè)客戶端OkHttpClient統(tǒng)一暴露出來。
OkHttpClient中全是一些配置,比如代理的配置、ssl證書的配置等。而Call本身是一個(gè)接口,我們獲得的實(shí)現(xiàn)為:RealCall
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;
}Call的execute代表了同步請求,而enqueue則代表異步請求。兩者唯一區(qū)別在于一個(gè)會(huì)直接發(fā)起網(wǎng)絡(luò)請求,而另一個(gè)使用OkHttp內(nèi)置的線程池來進(jìn)行。這就涉及到OkHttp的任務(wù)分發(fā)器。
- Call: 每一個(gè)請求的實(shí)例,比如登錄login 對應(yīng)一個(gè)Call、獲取用戶信息 對應(yīng)一個(gè)Call。Call本身就是一個(gè)接口,用戶的每一個(gè)Http請求就是一個(gè)Call實(shí)例,而且每一個(gè)Call都對應(yīng)一個(gè)線程。
- Call包含了 request()、execute()、enqueue() 方法。
- RealCall: 具體的Call接口實(shí)現(xiàn)類,代表每一個(gè)HTTP請求。每一個(gè)RealCall內(nèi)部有一個(gè)AsyncCall final類。
- AsyncCall: RealCall類的內(nèi)部final類,實(shí)現(xiàn)了NamedRunnable類的execute()。繼承于NamedRunnable類,NamedRunnable類實(shí)現(xiàn)了Runnable接口,并且有一個(gè)execute()抽象方法,這個(gè)抽象方法在Runnable的run()里執(zhí)行。
- Dispatcher:
- OkHttp的任務(wù)隊(duì)列,其內(nèi)部維護(hù)了一個(gè)線程池,進(jìn)行線程分發(fā),實(shí)現(xiàn)非阻塞,高可用,高并發(fā)。
- 當(dāng)有接收到一個(gè)Call時(shí),Dispatcher負(fù)責(zé)在線程池中找到空閑的線程并執(zhí)行其execute方法。
- Okhttp采用Deque作為緩存隊(duì)列,按照入隊(duì)的順序先進(jìn)先出。
- OkHttp最出彩的地方就是在try/finally中調(diào)用了finished函數(shù),可以主動(dòng)控制等待隊(duì)列的移動(dòng),而不是采用 鎖或者wait/notify,極大減少了編碼復(fù)雜性。
分發(fā)器
Dispatcher,分發(fā)器就是來調(diào)配請求任務(wù)的,內(nèi)部會(huì)包含一個(gè)線程池??梢栽趧?chuàng)建OkHttpClient時(shí),傳遞我們自己定義的線程池來創(chuàng)建分發(fā)器。
這個(gè)Dispatcher中的成員有:
//異步請求同時(shí)存在的最大請求 private int maxRequests = 64; //異步請求同一域名同時(shí)存在的最大請求 private int maxRequestsPerHost = 5; //閑置任務(wù)(沒有請求時(shí)可執(zhí)行一些任務(wù),由使用者設(shè)置) private @Nullable Runnable idleCallback; //異步請求使用的線程池 private @Nullable ExecutorService executorService; //異步請求等待執(zhí)行隊(duì)列 private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); //異步請求正在執(zhí)行隊(duì)列 private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); //同步請求正在執(zhí)行隊(duì)列 private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
同步請求
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}因?yàn)橥秸埱蟛恍枰€程池,也不存在任何限制。所以分發(fā)器僅做一下記錄。
異步請求
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}當(dāng)正在執(zhí)行的任務(wù)未超過最大限制64,同時(shí)runningCallsForHost(call) < maxRequestsPerHost同一Host的請求不超過5個(gè),則會(huì)添加到正在執(zhí)行隊(duì)列,同時(shí)提交給線程池。否則先加入等待隊(duì)列。
加入線程池直接執(zhí)行沒啥好說的,但是如果加入等待隊(duì)列后,就需要等待有空閑名額才開始執(zhí)行。因此每次執(zhí)行完一個(gè)請求后,都會(huì)調(diào)用分發(fā)器的finished方法
//異步請求調(diào)用
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
//同步請求調(diào)用
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
//不管異步還是同步,執(zhí)行完后都要從隊(duì)列移除(runningSyncCalls/runningAsyncCalls)
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
//異步任務(wù)和同步任務(wù)正在執(zhí)行的和
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
// 沒有任務(wù)執(zhí)行執(zhí)行閑置任務(wù)
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}需要注意的是 只有異步任務(wù)才會(huì)存在限制與等待,所以在執(zhí)行完了移除正在執(zhí)行隊(duì)列中的元素后,異步任務(wù)結(jié)束會(huì)執(zhí)行promoteCalls()。很顯然這個(gè)方法肯定會(huì)重新調(diào)配請求。
private void promoteCalls() {
//如果任務(wù)滿了直接返回
if (runningAsyncCalls.size() >= maxRequests) return;
//沒有等待執(zhí)行的任務(wù),返回
if (readyAsyncCalls.isEmpty()) return;
//遍歷等待執(zhí)行隊(duì)列
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
//等待任務(wù)想要執(zhí)行,還需要滿足:這個(gè)等待任務(wù)請求的Host不能已經(jīng)存在5個(gè)了
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}請求流程
用戶是不需要直接操作任務(wù)分發(fā)器的,獲得的RealCall中就分別提供了execute與enqueue來開始同步請求或異步請求。
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
//調(diào)用分發(fā)器
client.dispatcher().executed(this);
//執(zhí)行請求
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
//請求完成
client.dispatcher().finished(this);
}
}異步請求的后續(xù)同時(shí)是調(diào)用getResponseWithInterceptorChain()來執(zhí)行請求
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
//調(diào)用分發(fā)器
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}如果該RealCall已經(jīng)執(zhí)行過了,再次執(zhí)行是不允許的。異步請求會(huì)把一個(gè)AsyncCall提交給分發(fā)器。
AsyncCall實(shí)際上是一個(gè)Runnable的子類,使用線程啟動(dòng)一個(gè)Runnable時(shí)會(huì)執(zhí)行run方法,在AsyncCall中被重定向到execute方法:
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
//線程池執(zhí)行
@Override
protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
//.......
} catch (IOException e) {
//......
} finally {
//請求完成
client.dispatcher().finished(this);
}
}
}
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();
}同時(shí)AsyncCall也是RealCall的普通內(nèi)部類,這意味著它是持有外部類RealCall的引用,可以獲得直接調(diào)用外部類的方法。
可以看到無論是同步還是異步請求實(shí)際上真正執(zhí)行請求的工作都在getResponseWithInterceptorChain()中。這個(gè)方法就是整個(gè)OkHttp的核心:攔截器責(zé)任鏈。但是在介紹責(zé)任鏈之前,我們再來回顧一下線程池的基礎(chǔ)知識(shí)。
分發(fā)器線程池
前面我們提過,分發(fā)器就是來調(diào)配請求任務(wù)的,內(nèi)部會(huì)包含一個(gè)線程池。當(dāng)異步請求時(shí),會(huì)將請求任務(wù)交給線程池來執(zhí)行。那分發(fā)器中默認(rèn)的線程池是如何定義的呢?為什么要這么定義?
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(
0, //核心線程
Integer.MAX_VALUE, //最大線程
60, //空閑線程閑置時(shí)間
TimeUnit.SECONDS, //閑置時(shí)間單位
new SynchronousQueue<Runnable>(), //線程等待隊(duì)列
Util.threadFactory("OkHttp Dispatcher", false) //線程創(chuàng)建工廠
);
}
return executorService;
}為什么選擇使用OKHttp
1.可擴(kuò)展性高。類似于緩存,Dns,請求/連接/響應(yīng)超時(shí)時(shí)間等等都可以通過配置傳入,甚至線程池都可以根據(jù)自己的需求來配置。
2.OKHttp使用了連接池緩存,提高通信效率。
3.責(zé)任鏈五層攔截器模式,每層功能清晰明了,并且提供了兩層可擴(kuò)展的攔截器方便進(jìn)行所需要的改造。
4.層次結(jié)構(gòu)清晰,方便進(jìn)行問題的排查。
5.觀察者模式的充分使用,查看請求狀態(tài)和監(jiān)控請求狀態(tài)變得十分簡單。
6.使用了OKIO框架進(jìn)行數(shù)據(jù)的處理,效率和安全性上更高。
到此這篇關(guān)于OkHttp原理分析總結(jié)的文章就介紹到這了,更多相關(guān)OkHttp原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- OkHttp攔截器在Android網(wǎng)絡(luò)中的使用和工作原理
- Android入門之使用OKHttp多線程下載文件
- Android 使用 okhttp3和retrofit2 進(jìn)行單文件和多文件上傳
- Android基于OkHttp實(shí)現(xiàn)文件上傳功能
- Android使用OKhttp3實(shí)現(xiàn)登錄注冊功能+springboot搭建后端的詳細(xì)過程
- Android的簡單前后端交互(okHttp+springboot+mysql)
- Android Okhttp斷點(diǎn)續(xù)傳面試深入解析
- Android使用OkHttp發(fā)送post請求
- Android使用OkHttp進(jìn)行網(wǎng)絡(luò)同步異步操作
- Android視頻/音頻緩存框架AndroidVideoCache(Okhttp)詳解
- Android OkHttp實(shí)現(xiàn)全局過期token自動(dòng)刷新示例
相關(guān)文章
Android在fragment中編寫toobar的步驟詳解
這篇文章主要介紹了Android在fragment中編寫toobar,本文分步驟通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
Android 開發(fā)手機(jī)(三星)拍照應(yīng)用照片旋轉(zhuǎn)問題解決辦法
這篇文章主要介紹了Android 開發(fā)手機(jī)(三星)拍照應(yīng)用照片旋轉(zhuǎn)問題解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04
Android實(shí)現(xiàn)電子羅盤(指南針)方向傳感器的應(yīng)用
今天小編就為大家分享一篇關(guān)于Android實(shí)現(xiàn)電子羅盤(指南針)方向傳感器的應(yīng)用,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03
android基于SwipeRefreshLayout實(shí)現(xiàn)類QQ的側(cè)滑刪除
本篇文章主要介紹了android基于SwipeRefreshLayout實(shí)現(xiàn)類QQ的側(cè)滑刪除,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-10-10
全面解析Android的開源圖片框架Universal-Image-Loader
這篇文章主要介紹了Android的開源圖片框架Universal-Image-Loader,Universal-Image-Loader在GitHub上開源,其提供的圖片加載功能令人印象相當(dāng)深刻,需要的朋友可以參考下2016-04-04
Android 滑動(dòng)監(jiān)聽RecyclerView線性流+左右劃刪除+上下移動(dòng)
這篇文章主要介紹了Android 滑動(dòng)監(jiān)聽RecyclerView線性流+左右劃刪除+上下移動(dòng)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09

