概述
從OkHttp問(wèn)世以來(lái)秒裕,度娘速梗,google上關(guān)于OkHttp的講解說(shuō)明數(shù)不勝數(shù)籍茧,各種解讀思想不盡相同版述,一千個(gè)讀者就有一千個(gè)哈默雷特。本篇文章從源碼出發(fā)向你介紹Okhttp的基本使用以及底層實(shí)現(xiàn)原理寞冯,讓你從會(huì)寫轉(zhuǎn)向會(huì)用渴析,學(xué)習(xí)Android頂尖源碼的設(shè)計(jì)理念和開源擴(kuò)展性晚伙,如果解讀有誤,還望提出探討糾正俭茧。
工欲善其事咆疗,必先利其器
Android API23(6.0)版本以后,Google正式移除Apache-HttpClient 恢恼。OkHttp 作為一個(gè)現(xiàn)代民傻,快速,高效的HttpClient场斑,其功能之強(qiáng)大也是顯而易見的
1.支持SPDY 可以合并多個(gè)請(qǐng)求到同一個(gè)主機(jī)的請(qǐng)求漓踢、連接池、GZIP和HTTP緩存
2.支持HTTP/2協(xié)議漏隐,通過(guò)HTTP/2 可以讓客戶端中到服務(wù)器的所有請(qǐng)求共用同一個(gè)Socket連接
3.非HTTP/2 請(qǐng)求時(shí)喧半, OkHttp內(nèi)部會(huì)維護(hù)一個(gè)線程池,通過(guò)線程池可以對(duì)HTTP/1.x的連接進(jìn)行復(fù)用青责,減少延遲
4.支持post,get請(qǐng)求挺据,基于http的文件上傳和下載
5.默認(rèn)情況下,OkHttp會(huì)自動(dòng)處理常見的網(wǎng)絡(luò)問(wèn)題脖隶,像二次連接扁耐、SSL的握手問(wèn)題
當(dāng)然OkHttp的功能遠(yuǎn)不止這些,這里只是說(shuō)明平時(shí)經(jīng)常用到的产阱。既然OkHttp已經(jīng)作為官方庫(kù)使用婉称,相比我們?cè)谧鲰?xiàng)目的時(shí)候也會(huì)用,但對(duì)于其底層的實(shí)現(xiàn)原理還是一知半解构蹬,那我們就從這篇文章開始解釋其底層實(shí)現(xiàn)原理王暗。開車前先來(lái)一波介紹:
Ecplise引用:下載最新的Jar包
Android studio引用:implementation 'com.squareup.okhttp3:okhttp:3.11.0' //最新版本號(hào)請(qǐng)關(guān)注okhttp官網(wǎng)
Maven引用:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.11.0</version> //最新版本號(hào)請(qǐng)關(guān)注okhttp官網(wǎng)
</dependency>
各位老司機(jī)們,馬上開車庄敛,嘀嘀嘀俗壹!
一、基本使用方法
流程如下
// 啟動(dòng)客戶端類藻烤,主要有兩種方法進(jìn)行創(chuàng)建绷雏,new對(duì)象和Builder內(nèi)部類實(shí)現(xiàn)實(shí)例化
OkHttpClient client = new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
// get請(qǐng)求
// 通過(guò)Builder模式創(chuàng)建一個(gè)Request對(duì)象(即請(qǐng)求報(bào)文)
// 這里可以設(shè)置請(qǐng)求基本參數(shù):url地址,get請(qǐng)求怖亭,POST請(qǐng)求之众,請(qǐng)求頭,cookie參數(shù)等
Request request = new Request.Builder()
.url("http://www.baidu.com")
.header("User-Agent", "xxx.java")
.addHeader("token", "xxx")
.get()
.build();
// POST請(qǐng)求
// 表單形式上傳
RequestBody body = new FormBody.Builder().add("xxx","xxx").build();
// JSON參數(shù)形式依许,F(xiàn)ile對(duì)象上傳
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
RequestBody body = RequestBody.create(MediaType.parse("File/*"), file);
Request request = new Request.Builder()
.post(body)
.url(url)
.header("User-Agent", "xxx.java")
.addHeader("token", "xxx")
.build();
// 創(chuàng)建Call對(duì)象(Http請(qǐng)求) 棺禾,即連接Request和Response的橋梁
// newCall方法將request封裝成Call對(duì)象
Call call = client.newCall(request);
try{
// Response即響應(yīng)報(bào)文信息,包含返回狀態(tài)碼峭跳,響應(yīng)頭膘婶,響應(yīng)體等
Response response = call.execute();
// 這里深入一點(diǎn)缺前,Call其實(shí)是一個(gè)接口,調(diào)用Call的execute()發(fā)送同步請(qǐng)求其實(shí)是調(diào)用了Realcall實(shí)現(xiàn)類的方法悬襟,Realcall從源碼可以看出示一個(gè)Runable
System.out.println(response.body().string());
}catch(IOException e){
e.printStackTrace();
}
看完代碼你可能覺得OkHttp基本流程很繁瑣衅码,但是去掉一些擴(kuò)展參數(shù),你會(huì)發(fā)現(xiàn)OkHttp的使用其實(shí)很簡(jiǎn)單脊岳,無(wú)非就是
1.創(chuàng)建一個(gè)OkHttpClient并實(shí)例化逝段,可設(shè)置相關(guān)參數(shù)連接時(shí)長(zhǎng)connectTimeout等
2.創(chuàng)建一個(gè)Request對(duì)象并實(shí)例化,可設(shè)置網(wǎng)絡(luò)地址url割捅,請(qǐng)求方式get,post奶躯,攜帶參數(shù)等;
3.創(chuàng)建一個(gè)Call對(duì)象亿驾,通過(guò)okhttpClient的newCall()方法將Request封裝成Call對(duì)象
4.創(chuàng)建一個(gè)Response響應(yīng)嘹黔,用于接收服務(wù)器返回的相關(guān)信息;
即OkHttpClient客戶端通過(guò)newCall()方法接受你的Request請(qǐng)求并生成Response響應(yīng)
二莫瞬、同步和異步請(qǐng)求
看到這里你可能會(huì)問(wèn)儡蔓,為什么不繼續(xù)講些關(guān)于文件上傳,文件下載疼邀,Interceptors攔截器這些內(nèi)容喂江?其實(shí)同步和異步請(qǐng)求的實(shí)現(xiàn)可以說(shuō)是源碼中非常重要的一環(huán),涉及到線程池
旁振,Dispatch調(diào)度
开呐,反向代理
等,掌握核心科技规求,剩下的都是開胃小菜。
1)基本使用方法
同步請(qǐng)求方法
OkhttpClient client = new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
Request request = new Request.Builder()
.url("http://www.baidu.com")
.get()
.build();
Call call = client.newCall(request);
try{
Response response = call.execute();//調(diào)用同步請(qǐng)求
System.out.println(response.body().string());
}catch(IOException e){
e.printStackTrace();
}
通過(guò)上面代碼可以看出同步請(qǐng)求的基本流程:
1.創(chuàng)建OkHttpClient和Request對(duì)象
2.將Request封裝成Call對(duì)象
3.調(diào)用Call的excute()發(fā)起同步請(qǐng)求
*特別注意*:
當(dāng)前線程發(fā)送同步請(qǐng)求后卵惦,就會(huì)進(jìn)入阻塞狀態(tài)
阻肿,直到數(shù)據(jù)有響應(yīng)才會(huì)停止(和異步最大的不同點(diǎn)
)
異步請(qǐng)求方法
OkhttpClient client = new OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
Request request = new Request.Builder()
.url("http://www.baidu.com")
.get()
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() { //調(diào)用異步請(qǐng)求,CallBack用于請(qǐng)求結(jié)束以后來(lái)進(jìn)行接口回調(diào)
@Override
public void onFailure(Call call, IOException e) { System.out.println("Failure");}
@Override
public void onResponse(Call call, Response response) throw s IOException {
System.out.println(response.body().string());
}
});
通過(guò)上面代碼可以看出異步請(qǐng)求的基本流程:
1.創(chuàng)建OkHttpClient和Request對(duì)象
2.將Request封裝成Call對(duì)象
3.調(diào)用Call的enqueue發(fā)起異步請(qǐng)求
*特別注意*:
onFailure和onResponse都是執(zhí)行在子線程
中
不難看出,其實(shí)異步和同步請(qǐng)求的不同點(diǎn)
在于
1沮尿、發(fā)起請(qǐng)求方法調(diào)用
2丛塌、是否阻塞線程
到此,我們已經(jīng)熟悉了OkHttp的同步畜疾,異步請(qǐng)求方法的基本使用赴邻;不管同步還是異步的調(diào)用都需要先初始化OkHttpClient,創(chuàng)建Request ,調(diào)用OkHttpClient.newcall()封裝Call對(duì)象啡捶,但其內(nèi)部又是如何實(shí)現(xiàn)的呢姥敛?
同步請(qǐng)求執(zhí)行流程
第一步:初始化OkHttpClient(Builder builder)
public Builder() {
dispatcher = new Dispatcher(); // 調(diào)度分發(fā)器(核心之一)
protocols = DEFAULT_PROTOCOLS; // 協(xié)議
connectionSpecs = DEFAULT_CONNECTION_SPECS; // 傳輸層版本和連接協(xié)議
eventListenerFactory = EventListener.factory(EventListener.NONE); // 監(jiān)聽器
proxySelector = ProxySelector.getDefault(); // 代理選擇器
cookieJar = CookieJar.NO_COOKIES; // cookie
socketFactory = SocketFactory.getDefault(); // socket 工廠
hostnameVerifier = OkHostnameVerifier.INSTANCE; // 主機(jī)名字
certificatePinner = CertificatePinner.DEFAULT; // 證書鏈
proxyAuthenticator = Authenticator.NONE; // 代理身份驗(yàn)證
authenticator = Authenticator.NONE; // 本地省份驗(yàn)證
connectionPool = new ConnectionPool(); // 連接池(核心之一)
dns = Dns.SYSTEM;// 基礎(chǔ)域名
followSslRedirects = true;// 安全套接層重定向
followRedirects = true;// 本地重定向
retryOnConnectionFailure = true; // 連接失敗重試
connectTimeout = 10_000; // 連接超時(shí)時(shí)間
readTimeout = 10_000; // 讀取超時(shí)時(shí)間
writeTimeout = 10_000; // 寫入超時(shí)時(shí)間
pingInterval = 0; // 命令間隔
}
從這里看到,其實(shí)OkHttpClient的初始化已經(jīng)幫我們配置了基本參數(shù)瞎暑,我們也可以根據(jù)自身業(yè)務(wù)需求進(jìn)行相應(yīng)的參數(shù)設(shè)置(失敗重連彤敛,添加攔截器与帆,cookie等等),一般遇到創(chuàng)建對(duì)象需要大量參數(shù)時(shí)墨榄,推薦使用Builider模式鏈?zhǔn)秸{(diào)用完成參數(shù)初始化玄糟,具體使用可以去Android源碼中的AlertDialog、Notification中詳細(xì)了解袄秩;
這里我們重點(diǎn)注意兩個(gè)核心阵翎,Dispatcher和ConnectionPool,這兩點(diǎn)會(huì)在后面做詳細(xì)講解
Dispatcher
:OkHttp請(qǐng)求的調(diào)度分發(fā)器之剧,由它決定異步請(qǐng)求在線程池中是直接處理還是緩存等待郭卫,當(dāng)然對(duì)于同步請(qǐng)求,只是將相應(yīng)的同步請(qǐng)求放到請(qǐng)求隊(duì)列當(dāng)中執(zhí)行
ConnectionPool
: 統(tǒng)一管理客戶端和服務(wù)器之間連接的每一個(gè)Connection猪狈,作用在于
1)當(dāng)你的Connection請(qǐng)求的URL相同時(shí)箱沦,可以選擇是否復(fù)用;2)控制Connection保持打開狀態(tài)還是復(fù)用
第二步:創(chuàng)建 (Builder builder)
public static class Builder {
HttpUrl url;
String method;
Headers.Builder headers;
RequestBody body; //請(qǐng)求體
Object tag; //標(biāo)簽
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder(); //Headers內(nèi)部類
}
這個(gè)構(gòu)造方法很簡(jiǎn)單雇庙,在Request.Builder模式下默認(rèn)指定請(qǐng)求方式為GET請(qǐng)求谓形,創(chuàng)建了Headers內(nèi)部類來(lái)保存頭部信息,我們?cè)賮?lái)看build方法
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
Request的構(gòu)造方法就是為其初始化指定需求的請(qǐng)求方式疆前,請(qǐng)求URL寒跳,請(qǐng)求頭部信息,這樣就完成同步請(qǐng)求的前兩步
第三步:調(diào)用OkHttpClient.newcall()封裝Call對(duì)象
/**
* Prepares the {@code request} to be executed at some point in the future.
* 準(zhǔn)備在將來(lái)某個(gè)時(shí)候執(zhí)行{@code請(qǐng)求}
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
上面我們也提到過(guò)竹椒,Call是一個(gè)接口童太,所以它的實(shí)際操作是在RealCall類中實(shí)現(xiàn)的
final class RealCall implements Call {
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor; //重定向攔截器
/**
* There is a cycle between the {@link Call} and {@link EventListener} that makes this awkward.
* This will be set after we create the call instance then create the event listener instance.
*/
private EventListener eventListener;
/** The application's original request unadulterated by redirects or auth headers. */
final Request originalRequest;
final boolean forWebSocket;
// Guarded by this.
private boolean executed;
//實(shí)際構(gòu)造方法
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
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;
}
}
從這里就可以看到,RealCall其實(shí)是持有之前初始化好的OkHttpClient和Request對(duì)象胸完,同時(shí)賦值了RetryAndFollowUpInterceptor重定向攔截器书释,關(guān)于攔截器的內(nèi)容,我們會(huì)后面具體講解OKhttp內(nèi)部的5大攔截器赊窥;
第四步爆惧,調(diào)用call.exucte方法實(shí)現(xiàn)同步請(qǐng)求
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace(); // 捕捉異常堆棧信息
eventListener.callStart(this); // 調(diào)用監(jiān)聽方法
try {
client.dispatcher().executed(this); // 調(diào)度器將call請(qǐng)求 加入到了同步執(zhí)行隊(duì)列中
Response result = getResponseWithInterceptorChain(); // 獲取返回?cái)?shù)據(jù)
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
首先, 加入了synchronized 同步鎖,判斷executed標(biāo)識(shí)位是否為true锨能,確保每個(gè)call只能被執(zhí)行一次不能重復(fù)執(zhí)行扯再,然后開啟了eventListener監(jiān)聽事件,接收相應(yīng)的事件回調(diào)址遇,通過(guò)dispatcher將Call請(qǐng)求添加到同步隊(duì)列中
public Dispatcher dispatcher() {
return dispatcher;
}
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
// 同步請(qǐng)求隊(duì)列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
每當(dāng)調(diào)用executed同步方法時(shí)熄阻,dispather就會(huì)幫我們把同步請(qǐng)求添加到同步請(qǐng)求隊(duì)列中去,由此可以看出Dispather調(diào)度器的作用就是維持Call請(qǐng)求發(fā)送狀態(tài)
和維護(hù)線程池
并把Call請(qǐng)求添加到相應(yīng)的執(zhí)行隊(duì)列
當(dāng)中倔约,由它決定當(dāng)前Call請(qǐng)求是緩存等待還是直接執(zhí)行,流程如下
getResponseWithInterceptorChain()是一個(gè)攔截器鏈秃殉,依次調(diào)用攔截器對(duì)返回的response進(jìn)行相應(yīng)的操作,我們?cè)谥v解到責(zé)任鏈模式時(shí)會(huì)詳細(xì)介紹,如圖
另外要特別注意下client.dispatcher().finished(this);
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false); // 注意參數(shù)傳遞的值
}
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(); // 將等待隊(duì)列的請(qǐng)求加入運(yùn)行隊(duì)列并開始執(zhí)行,只會(huì)在異 步方法中調(diào)用
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
/***********************************************************************************************************************/
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
當(dāng)同步請(qǐng)求完成后會(huì)調(diào)用finished()方法將隊(duì)列中的請(qǐng)求清除掉复濒,runningCallsCount()計(jì)算返回正在執(zhí)行同步請(qǐng)求和正在執(zhí)行異步請(qǐng)求的數(shù)量總和脖卖,最后判斷如果runningCallsCount 為0的時(shí)候,表示整個(gè)Dispatcher分發(fā)器中沒有可運(yùn)行的請(qǐng)求巧颈,同時(shí)在滿足idleCallback不為空的情況下畦木,就調(diào)用Run方法開啟閑置接口;這里可以看出砸泛,在同步請(qǐng)求的方法中十籍,dispatcher的作用只是調(diào)用 executed將Call請(qǐng)求添加到同步隊(duì)列中,執(zhí)行完畢后調(diào)用 finished清除隊(duì)列中的請(qǐng)求唇礁,可見dispatcher更多的是為異步服務(wù)
異步請(qǐng)求執(zhí)行流程
關(guān)于OkHttpClient和Request初始化流程上文已經(jīng)講解勾栗,不清楚的可以返回去看看,所以直奔主題
第四步盏筐,調(diào)用call.enqueue方法實(shí)現(xiàn)異步請(qǐng)求
//RealCall實(shí)現(xiàn)類
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true; // executed用于表示Call請(qǐng)求是否執(zhí)行過(guò)
}
captureCallStackTrace();// 捕捉異常堆棧信息
eventListener.callStart(this);// 開啟監(jiān)聽事件
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
有沒有發(fā)現(xiàn)和同步的excute方法很類似围俘,都是先使用synchronized 防止請(qǐng)求重復(fù)執(zhí)行,然后開啟監(jiān)聽事件琢融,最后在執(zhí)行相應(yīng)的方法界牡,但奇怪的是同步在執(zhí)行完excute方法后是直接通過(guò)getResponseWithInterceptorChain()返回?cái)?shù)據(jù),異步又是如何返回?cái)?shù)據(jù)的呢漾抬?AsyncCall又是干什么的宿亡?
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
@Override protected void execute() {
boolean signalledCallback = false;
try {
// 返回?cái)?shù)據(jù)
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);
}
}
}
這里的 AsyncCall 是 RealCall 的一個(gè)內(nèi)部類,它繼承于NamedRunnable抽象類纳令,NamedRunnable抽象類又實(shí)現(xiàn)了 Runnable挽荠,所以可以被提交到ExecutorService上執(zhí)行,在execute方法里平绩,我們看到了熟悉的流程,上文也說(shuō)到getResponseWithInterceptorChain是一個(gè)攔截器鏈圈匆,會(huì)依次執(zhí)行相應(yīng)的攔截器后返回?cái)?shù)據(jù),所以當(dāng)返回?cái)?shù)據(jù)后捏雌,通過(guò)retryAndFollowUpInterceptor重定向攔截器判斷請(qǐng)求是否正常執(zhí)行跃赚,并且通過(guò)Callback接口返回相應(yīng)數(shù)據(jù)信息,最后調(diào)用finished方法清除隊(duì)列
這里有個(gè)疑問(wèn)腹忽,Dispatcher是通過(guò)什么把異步就緒隊(duì)列的請(qǐng)求調(diào)度分發(fā)
到異步執(zhí)行隊(duì)列中的?
還記得我們講client.dispatcher().finished(this)的時(shí)候,說(shuō)到過(guò)promoteCalls方法砚作,只是同步傳參的是false沒有調(diào)用窘奏,但異步傳參是true,所以promoteCalls方法才真正在異步中調(diào)用
//Disaptcher
/** Ready async calls in the order they'll be run. */
// 異步就緒隊(duì)列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
// 異步執(zhí)行隊(duì)列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
private void promoteCalls() {
// maxRequests最大請(qǐng)求數(shù)量64
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) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
源碼是不是很清楚明了葫录,原來(lái)異步隊(duì)列就是在這里進(jìn)行調(diào)度的着裹,在for循環(huán)中,Disaptcher首先對(duì)異步就緒隊(duì)列進(jìn)行遍歷米同,如果滿足runningCallsForHost(當(dāng)前調(diào)用請(qǐng)求主機(jī)數(shù))小于maxRequestsPerHost( 最大請(qǐng)求主機(jī)數(shù)5個(gè))并且異步并發(fā)數(shù)量沒有超過(guò)最大請(qǐng)求數(shù)量64的前提下骇扇,就把異步就緒隊(duì)列中最后一個(gè)元素移除加入到異步執(zhí)行隊(duì)列中
我們接著看enqueue方法具體做了哪些操作
//Disaptcher
synchronized void enqueue(AsyncCall call) {
// 異步并發(fā)請(qǐng)求數(shù)量不能超過(guò)最大請(qǐng)求數(shù)量64
// 當(dāng)前網(wǎng)絡(luò)請(qǐng)求的host是否小于5個(gè)請(qǐng)求的host
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
// 加入執(zhí)行隊(duì)列 并交給線程池執(zhí)行
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
// 加入就緒隊(duì)列等待
readyAsyncCalls.add(call);
}
}
***************************************************************************************************************
public synchronized ExecutorService executorService() {
// 核心線程 最大線程 非核心線程閑置60秒回收 任務(wù)隊(duì)列
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
Disaptcher的enqueue方法只是做了一個(gè)異步請(qǐng)求的邏輯判斷摔竿,即判斷當(dāng)前異步并發(fā)執(zhí)行隊(duì)列的數(shù)量是否超過(guò)最大承載運(yùn)行數(shù)量64和相同host主機(jī)最多允許5條線程同時(shí)執(zhí)行請(qǐng)求,滿足以上條件少孝,則將傳進(jìn)來(lái)的AsyncCall添加到異步執(zhí)行隊(duì)列继低,同時(shí)啟動(dòng)線程池執(zhí)行,反之則添加到異步就緒隊(duì)列中等待稍走,executorService調(diào)用的就是AsyncCall的execute方法
同步和異步請(qǐng)求的源碼就講到這里袁翁,對(duì)過(guò)程還有不理解的可以在下方評(píng)論中提出問(wèn)題,下一篇我們接著講OkHttp的責(zé)任鏈模式婿脸,未完待續(xù)...