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
的話會很浪費(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,連接不成功會自動再次連接。
第二個(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í)例對象,不過他們共用很多其他的資源,不會對資源造成浪費(fèi)。
關(guān)于 OkHttpClient 的配置改變都在 Builder 中進(jìn)行
不需要了可以關(guān)閉
其實(shí)持有的線程池和連接池將會被自定釋放如果他們保持閑置的話。
你也可以自動釋放,釋放后將來再調(diào)用 call 的時(shí)候會被拒接。
client.dispatcher().excurorService().shutdown()
清除連接池,注意清除后,連接池的守護(hù)線程可能會立刻退出。
client.connectionPool().evictAll()
如果 Client 有緩存,可以關(guān)閉。注意:再次調(diào)用一個(gè)被關(guān)閉的 cache 會發(fā)生錯(cuò)誤。也會造成 crash。
client.cache().close();
OkHttp 在 HTTP/2 連接的時(shí)候也會使用守護(hù)線程。他們閑置的時(shí)候?qū)⒆詣油顺觥?/p>
知道有這么一回事就行,一般不會主動調(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)不會是 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 可用的話,他們會被自動添加到請求頭中。
來看一下這種類型的請求報(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ù)值用等號連接,參數(shù)之間用分號分隔。參數(shù)值用雙引號括起來
// 比如這樣,就是這種固定的格式 "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è)會直接發(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ù),可以主動控制等待隊(duì)列的移動,而不是采用 鎖或者wait/notify,極大減少了編碼復(fù)雜性。
分發(fā)器
Dispatcher
,分發(fā)器就是來調(diào)配請求任務(wù)的,內(nèi)部會包含一個(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è),則會添加到正在執(zhí)行隊(duì)列,同時(shí)提交給線程池。否則先加入等待隊(duì)列。
加入線程池直接執(zhí)行沒啥好說的,但是如果加入等待隊(duì)列后,就需要等待有空閑名額才開始執(zhí)行。因此每次執(zhí)行完一個(gè)請求后,都會調(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ù)才會存在限制與等待,所以在執(zhí)行完了移除正在執(zhí)行隊(duì)列中的元素后,異步任務(wù)結(jié)束會執(zhí)行promoteCalls()
。很顯然這個(gè)方法肯定會重新調(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í)行是不允許的。異步請求會把一個(gè)AsyncCall
提交給分發(fā)器。
AsyncCall
實(shí)際上是一個(gè)Runnable
的子類,使用線程啟動一個(gè)Runnable
時(shí)會執(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ǔ)知識。
分發(fā)器線程池
前面我們提過,分發(fā)器就是來調(diào)配請求任務(wù)的,內(nèi)部會包含一個(gè)線程池。當(dāng)異步請求時(shí),會將請求任務(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自動刷新示例
相關(guān)文章
Android在fragment中編寫toobar的步驟詳解
這篇文章主要介紹了Android在fragment中編寫toobar,本文分步驟通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01Android 開發(fā)手機(jī)(三星)拍照應(yīng)用照片旋轉(zhuǎn)問題解決辦法
這篇文章主要介紹了Android 開發(fā)手機(jī)(三星)拍照應(yīng)用照片旋轉(zhuǎn)問題解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04Android實(shí)現(xiàn)電子羅盤(指南針)方向傳感器的應(yīng)用
今天小編就為大家分享一篇關(guān)于Android實(shí)現(xiàn)電子羅盤(指南針)方向傳感器的應(yīng)用,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03android基于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-04Android 滑動監(jiān)聽RecyclerView線性流+左右劃刪除+上下移動
這篇文章主要介紹了Android 滑動監(jiān)聽RecyclerView線性流+左右劃刪除+上下移動的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09