欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

OkHttp原理分析小結(jié)

 更新時(shí)間:2024年01月17日 14:32:00   作者:Young丶  
OkHttp 是 Square 公司開源的一款網(wǎng)絡(luò)框架,封裝了一個(gè)高性能的 http 請求庫,本文對OkHttp原理給大家詳細(xì)講解,感興趣的朋友跟隨小編一起看看吧

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è)類

image-20221108142020997

這個(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();

image-20221108142440493

這是它的 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-cache

key0=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í)候引入了 boundaryboundary使得 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: 65744

fdPNG
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架構(gòu)圖

OKHttp發(fā)送主體流程

image-20221108145253547

在使用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;
}

Callexecute代表了同步請求,而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中就分別提供了executeenqueue來開始同步請求或異步請求。

@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)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論