OKHTTP異步和同步請(qǐng)求簡(jiǎn)單分析
OKHTTP攔截器緩存策略CacheInterceptor的簡(jiǎn)單分析
OKHTTP攔截器ConnectInterceptor的簡(jiǎn)單分析
OKHTTP攔截器CallServerInterceptor的簡(jiǎn)單分析
OKHTTP攔截器BridgeInterceptor的簡(jiǎn)單分析
OKHTTP攔截器RetryAndFollowUpInterceptor的簡(jiǎn)單分析
OKHTTP結(jié)合官網(wǎng)示例分析兩種自定義攔截器的區(qū)別
同步請(qǐng)求就是執(zhí)行請(qǐng)求的操作是阻塞式,直到 HTTP 響應(yīng)返回。它對(duì)應(yīng) OKHTTP 中的 execute 方法。
異步請(qǐng)求就類似于非阻塞式的請(qǐng)求,它的執(zhí)行結(jié)果一般都是通過接口回調(diào)的方式告知調(diào)用者。它對(duì)應(yīng) OKHTTP 中的 enqueue 方法。
示例代碼
下面的代碼演示了如何進(jìn)行同步和異步請(qǐng)求的操作。
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url("http://www.qq.com")
.build();
Call call = okHttpClient.newCall(request);
//1.異步請(qǐng)求,通過接口回調(diào)告知用戶 http 的異步執(zhí)行結(jié)果
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println(e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
System.out.println(response.body().string());
}
}
});
//2.同步請(qǐng)求
//Response response = call.execute();
//if (response.isSuccessful()) {
// System.out.println(response.body().string());
//}
異步請(qǐng)求的基本原理

Call
負(fù)責(zé)準(zhǔn)備去執(zhí)行一個(gè) request 請(qǐng)求,一個(gè) call 只能負(fù)責(zé)去執(zhí)行一個(gè)請(qǐng)求,不能被執(zhí)行兩次。因?yàn)?OkHttpClient 是實(shí)現(xiàn)了 Call.Factory 因此它具備創(chuàng)建 Call 對(duì)象的功能,內(nèi)部創(chuàng)建的就是 RealCall 對(duì)象。Call 是封裝 request 的,它表示一個(gè)可以執(zhí)行的請(qǐng)求。
Call 的實(shí)現(xiàn)類 RealCall
因?yàn)?Call 是接口,內(nèi)部定義了同步與異步的請(qǐng)求,以及取消請(qǐng)求等操作,這些操作是由 RealCall 真正去實(shí)現(xiàn)的。
在 RealCall 中關(guān)鍵的幾個(gè)屬性:
client 就是我們?cè)谕饨鐒?chuàng)建的 OkHttpClient 對(duì)象,通過這個(gè) client 就是調(diào)用 Dispatcher 去分發(fā)請(qǐng)求任務(wù)。
executed 它是 boolean 類型,上面介紹 Call 時(shí)已經(jīng)說明了,一個(gè) Call 只能被執(zhí)行一次,在內(nèi)部就是通過這個(gè)屬性進(jìn)行判斷的。
originalRequest Request 對(duì)象,它就是通過 okHttpClient.newCall(request) 傳入的 Request 對(duì)象,這個(gè) Request 在整個(gè)網(wǎng)絡(luò)請(qǐng)求起到非常重要的作用,它會(huì)被傳入到各個(gè) Interceptor 中去。例如用戶創(chuàng)建的 request 對(duì)象,只是簡(jiǎn)單的設(shè)置了 url ,method,requestBody 等參數(shù),但是想要發(fā)送一個(gè)網(wǎng)絡(luò)請(qǐng)求這樣簡(jiǎn)單地配置還是不夠的,系統(tǒng)提供的攔截器 BridgeInterceptor 就是負(fù)責(zé)做這件事,它會(huì)為該請(qǐng)求添加請(qǐng)求頭,例如 gzip,cookie,content-length 等,簡(jiǎn)單說它會(huì)將用戶創(chuàng)建的 request 添加一些參數(shù)從而使其更加符合向網(wǎng)絡(luò)請(qǐng)求的 request 。其他攔截器的功能也是對(duì) request 進(jìn)行操作,具體看源碼。
Dispatcher 相關(guān)知識(shí)點(diǎn)
異步任務(wù)分發(fā)器,它會(huì)內(nèi)部指定線程池去執(zhí)行異步任務(wù),并在執(zhí)行完畢之后提供 finish 方法結(jié)束異步請(qǐng)求之后從等待隊(duì)列中獲取下一個(gè)滿足條件的異步任務(wù)去執(zhí)行。
1、在 Dispatcher 有幾個(gè)比較重要的屬性,這幾個(gè)屬性會(huì)影響異步請(qǐng)求的執(zhí)行。
int maxRequests = 64 會(huì)去指定并發(fā) call 的最大個(gè)數(shù)。
maxRequestsPerHost = 5: 每個(gè)主機(jī)最大請(qǐng)求數(shù)為5 ,也就是最多 5 個(gè)call公用一個(gè) host。這個(gè)host 就是在 RealCall 中通過 originalRequest.url().host() 去獲取的,例如 www.baidu.com
executorService 就是執(zhí)行異步任務(wù)的線程池,在內(nèi)部中已經(jīng)指定好了線程池,當(dāng)然也可以在 Dispacther 中通過構(gòu)造方法去指定一個(gè)線程池。
Deque<AsyncCall> readyAsyncCalls 表示在隊(duì)列中已經(jīng)準(zhǔn)備好的請(qǐng)求。
Deque<AsyncCall> runningAsyncCalls 正在執(zhí)行的異步請(qǐng)求,包括已經(jīng)取消的請(qǐng)求(還沒有執(zhí)行finish操作的請(qǐng)求。)
Deque<RealCall> runningSyncCalls 正在運(yùn)行的同步請(qǐng)求,包括已經(jīng)取消的請(qǐng)求(還沒有執(zhí)行finish操作的請(qǐng)求。)
2、關(guān)于 Dispatcher 的功能在下面的異步和同步請(qǐng)求中我們?cè)僖灰惶剿鳌?/p>
Call 的實(shí)現(xiàn)者 RealCall
它具備有異步和同步請(qǐng)求,還有取消請(qǐng)求的功能,它內(nèi)部有一個(gè) AsyncCall 內(nèi)部類,在 Dispatcher 中分發(fā)的異步請(qǐng)求任務(wù)就是 AsyncCall 。這里分發(fā)的任務(wù)指的是異步任務(wù),而不是同步任務(wù)。AsyncCall 就是用表示一個(gè)異步任務(wù)的,在 Dispatcher 內(nèi)部有維護(hù)了兩個(gè)隊(duì)列來存儲(chǔ) AsyncCall,分別是 readyAsyncCalls 和
runningAsyncCalls 它們分別表示準(zhǔn)備要執(zhí)行的 AsyncCall 隊(duì)列和正在執(zhí)行的 AsycnCall 隊(duì)列。當(dāng)然還有一個(gè) runningSyncCalls 這個(gè)隊(duì)列,但是它適用于存放 RealCall ,也就是用于存儲(chǔ)同步請(qǐng)求的任務(wù)。
- 在 RealCall 實(shí)現(xiàn)異步請(qǐng)求 call.enqueue(new Callback())
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
//檢測(cè)該 call 是否被執(zhí)行過了,如果已經(jīng)執(zhí)行了,那么就拋出異常
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//關(guān)鍵代碼:將 AsycnCall 添加到隊(duì)列中。將任務(wù)交給 Dispatcher 去執(zhí)行。
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
- 使用 Dispatcher 去分發(fā)一個(gè)異步任務(wù)
合理性的校驗(yàn)操作,我們?cè)诮榻B Dispacther 的相關(guān)屬性時(shí)已經(jīng)說明,在 OKHTTP 中正在執(zhí)行的請(qǐng)求不能超過 64 個(gè),并且同一個(gè)主機(jī)不能超過 5 請(qǐng)求,當(dāng)滿足這兩個(gè)條件,即可將任務(wù)添加到正在執(zhí)行的隊(duì)列 runningAsyncCalls 中,并且通知線程池安排線程去執(zhí)行這個(gè)異步任務(wù),否則就會(huì)被添加到等待隊(duì)列中 readyAsyncCalls。
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//當(dāng)正在執(zhí)行的請(qǐng)求小于64個(gè)&&該 call 對(duì)應(yīng)的主機(jī)少于5個(gè) Call 時(shí)
//將任務(wù)添加到 runningAsycnCalls 中,標(biāo)記為正在執(zhí)行的任務(wù)。
runningAsyncCalls.add(call);
//在線程池中執(zhí)行這個(gè)任務(wù)。
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
真正的異步執(zhí)行者 AsyncCall
在前面提到了 AsyncCall 表示的是一個(gè)異步任務(wù),在使用 Dispatcher 會(huì)將 AsyncCall 交給指定的線程去執(zhí)行,而 AsyncCall 是 NamedRunnable 的子類,因此它也具備 Runnble 的特性,換句話說,在線程池中執(zhí)行的任務(wù)就是 AsyncCall 了。
當(dāng)線程池執(zhí)行這個(gè)異步任務(wù)時(shí),那么該 Runnable 的 run 方法就會(huì)被執(zhí)行,我們查閱了源碼,在 run 方法內(nèi)部會(huì)去調(diào)用 AsyncCall 的 execute 方法。
@Override 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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
總結(jié)該方法中它主要做了這 3 件事:
得到 HTTP 請(qǐng)求的響應(yīng) Response :Response response = getResponseWithInterceptorChain();得到一個(gè) response 響應(yīng)。(這個(gè)是一個(gè)遞歸的調(diào)用過程,具體在其他博客中再分析具體實(shí)現(xiàn)。)
給調(diào)用進(jìn)行接口回調(diào)異步任務(wù)執(zhí)行的結(jié)果:responseCallback.onResponse 或者 responseCallback.onFailure
結(jié)束該請(qǐng)求,并且執(zhí)行下一個(gè)等待的異步任務(wù):client.dispatcher().finished(this);
對(duì)于 AsyncCall 中所做的這 3 步中,前面兩步都比較好理解,下面主要看看它是如何結(jié)束一個(gè)請(qǐng)求的并且開啟下一個(gè)異步請(qǐng)求的?
我們?cè)谇懊娼榻B Dispacther 已經(jīng)了解了它的作用,這里再次強(qiáng)調(diào)一下,它是負(fù)責(zé)去分發(fā)一個(gè)異步任務(wù)給指定的線程池去執(zhí)行,并且可以在執(zhí)行完畢之后去等待隊(duì)列中獲取下一個(gè)請(qǐng)求去執(zhí)行。
在 AsyncCall 中的 execute 中執(zhí)行一個(gè)異步請(qǐng)求,注意在 finally 塊內(nèi)部調(diào)用了 client.dispatcher().finished(this);它的作用是通知 Dispatcher 我的任務(wù)執(zhí)行完畢了,你可以將我從集合中移除了,開啟下一個(gè)異步任務(wù)吧。下面就是 finish 的源碼:
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
總結(jié)該方法中它主要做了這 3 件事:
從 calls 中移除該 AysyncCall 對(duì)象,而這個(gè) calls 就是 Dispatcher 中的 runningAsyncCalls。
promoteCalls() 調(diào)用該方法就可以實(shí)現(xiàn)從等待隊(duì)列中取出下一個(gè)異步任務(wù)去執(zhí)行。
idleCallback.run(); 沒有正在執(zhí)行的任務(wù)時(shí),那么就回調(diào)這個(gè)接口,該回調(diào)接口需要通過 setIdleCallback 方法傳遞進(jìn)來。它可以通知當(dāng)沒有任務(wù)正在執(zhí)行時(shí),通知外界。runningCallsCount 這個(gè)是同步執(zhí)行的任務(wù)數(shù)和異步執(zhí)行任務(wù)數(shù)之和。
通過 promoteCalls() 去執(zhí)行下一個(gè)異步任務(wù)
該方法是用于在等待隊(duì)列中獲取下一個(gè)異步任務(wù)去執(zhí)行。在內(nèi)部會(huì)還是會(huì)對(duì) Dispatcher 內(nèi)部的幾個(gè)屬性進(jìn)行判斷,例如對(duì)正在執(zhí)行的請(qǐng)求數(shù)量是否超過了 64 個(gè),還有遍歷等待隊(duì)列里的所有的 AsyncCall ,每遍歷出一個(gè) AsycnCall 都校驗(yàn)它的主機(jī)是否有超過 5 個(gè)正在執(zhí)行的異步請(qǐng)求在使用了,在滿足條件的情況下,就馬上會(huì)線程池去執(zhí)行這個(gè)任務(wù),以此類推,任務(wù)就這樣一個(gè)一個(gè)的被執(zhí)行。
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
//滿足下一個(gè)要執(zhí)行的任務(wù)的要求。
i.remove();
//添加到正在請(qǐng)求隊(duì)列中
runningAsyncCalls.add(call);
//由線程池去執(zhí)行這個(gè)任務(wù)。
executorService().execute(call);
}
//超過了 64 個(gè)請(qǐng)求那么就直接 return
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
使用 RealCall 實(shí)現(xiàn)的同步請(qǐng)求
上面所描述的都是異步請(qǐng)求,現(xiàn)在來看看同步請(qǐng)求。
同步請(qǐng)求調(diào)用的是 execute 方法,在內(nèi)部會(huì)調(diào)用 client.dispatcher().executed(this); 方法,進(jìn)去看源碼可知道它實(shí)際就是將 RealCall 添加到 Dispatcher 的 runningSyncCalls 中,表示當(dāng)前正在執(zhí)行的同步隊(duì)列中。在這里使用 Dispacther 的中 execute 僅僅只是將其添加到集合中而已,沒有作別的操作,而真正執(zhí)行同步任務(wù)的核心代碼是 getResponseWithInterceptorChain(); ,該方法負(fù)責(zé)去網(wǎng)絡(luò)請(qǐng)求,并且得到一個(gè)響應(yīng),具體內(nèi)部怎么實(shí)現(xiàn)日后再分析。在最后的 finally 代碼塊執(zhí)行的功能跟異步任務(wù)一樣,也是通過 Dispatcher 去 finish 該請(qǐng)求。
在 finish 中雖然同步和異步執(zhí)行的方法是一樣的,但是執(zhí)行流程并不一樣,異步任務(wù)需要通過 promoteCalls 去執(zhí)行下一個(gè)異步任務(wù),而同步請(qǐng)求是不需要的,這個(gè)的判斷標(biāo)記就 private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {...} 就是第三個(gè)參數(shù),當(dāng)該 promoteCalls 為 false 表示同步請(qǐng)求,true 表示異步請(qǐng)求,其他操作都是和異步請(qǐng)求是一樣的。
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}