看了有一段時間的OkHttp3的源碼了宁舰。今天動筆開始寫一寫掌实,本篇文章只是簡單的寫一下OkHttp3的一個過程盖高。(以后的文章會對OkHttp3的內(nèi)部進行分析)矢否。
OkHttp3 優(yōu)點:
1.支持http1/http2
2.對一臺機器的所有請求共享同一個socket
3.內(nèi)部有連接池仲闽,減少創(chuàng)建和鏈接時過多的時間消耗
設(shè)計模式
整個OkHttp用到很多設(shè)計模式:
1.外觀模式:
OKHttpClient 里面封裝了很多的類對象。其實就是將OKHttp的很多功能模塊僵朗,全部封裝到這個類中赖欣,讓這個類單獨提供對外的API。
外觀模式(Facade Pattern)隱藏系統(tǒng)的復(fù)雜性验庙,并向客戶端提供了一個客戶端可以訪問系統(tǒng)的接口顶吮。這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式,它向現(xiàn)有的系統(tǒng)添加一個接口粪薛,來隱藏系統(tǒng)的復(fù)雜性悴了。
這種模式涉及到一個單一的類,該類提供了客戶端請求的簡化方法和對現(xiàn)有系統(tǒng)類方法的委托調(diào)用违寿。
2.建造者模式
正因為內(nèi)部功能塊比較多湃交,大量使用了建造者模式,比如Reuqest的創(chuàng)建,等等吧藤巢。
建造者模式(Builder Pattern)使用多個簡單的對象一步一步構(gòu)建成一個復(fù)雜的對象搞莺。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式掂咒。
一個 Builder 類會一步一步構(gòu)造最終的對象才沧。該 Builder 類是獨立于其他對象的迈喉。
主體架構(gòu)和大概流程
OkHttp的主要使用就是:
new OkHttpClient().newCall(request).execute();(同步);
new OkHttpClient().newCall(request).enqueue();(異步)
通過這行代碼温圆,我們可以捋出OkHttp的大致流程:
execute同步請求的方法:
newCall(request)的方法是返回一個RealCall
@Override public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//是用來跟蹤調(diào)用棧的信息的挨摸,不用深究
captureCallStackTrace();
try {
A://此方法只是把請求加入隊列并沒有真正執(zhí)行;
client.dispatcher().executed(this);
B://真正執(zhí)行請求進行網(wǎng)絡(luò)請求返回結(jié)果
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
A:此處調(diào)用了Dispatcher的executed的方法 把Call加入到隊列中runningSyncCalls.add(call);(稍后分析Dispatcher)
B:調(diào)用攔截器返回結(jié)果捌木;
enqueue異步請求的方法:
調(diào)用RealCall的enqueue方法
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
A:
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
A:調(diào)用了Dispatcher的enqueue方法油坝。可以看到此方法參數(shù)中創(chuàng)建了一個AsyncCall(構(gòu)建call對象)刨裆。
其中Dispatcher的enqueue的方法
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
可以看出此段代碼也是把請求加入隊列澈圈,然后執(zhí)行 executorService().execute(call),其中AsyncCall 繼承NamedRunnable 是一個線程帆啃,我們應(yīng)該看他的execute()方法瞬女,此方法中也同樣調(diào)用了 Response response = getResponseWithInterceptorChain();方法。
executorService屬于線程池努潘,所以此方法executorService().execute(call)執(zhí)行的是AsyncCall方法的execute方法诽偷;(稍后會對Dispatcher進行分析)
Dispatcher(任務(wù)分發(fā)器)
我們知道OkHttp內(nèi)部是有一個線程池的,這個線程池就在Dispatcher中疯坤,其實這個類就是一個任務(wù)隊列报慕。
那么我們來看一下Dispatcher的成員變量:
//最大并發(fā)請求數(shù)為64
private int maxRequests = 64;
//每個主機最大請求數(shù)是5
private int maxRequestsPerHost = 5;
//線程
private @Nullable Runnable idleCallback;
//線程池
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
//準備執(zhí)行的異步請求隊列,對象是異步請求
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//正在執(zhí)行的異步請求隊列压怠,其中包括了已經(jīng)取消了但是還未執(zhí)行完的請求
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//正在執(zhí)行的同步請求隊列眠冈,同樣包括了已經(jīng)取消了但是還未執(zhí)行完的請求
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
看完成員變量我們發(fā)現(xiàn)其中有兩個異步隊列,這是為什么菌瘫?
采用Deque作為緩存蜗顽,按照入隊的順序先進先出,Deque雙端隊列雨让,繼承自Queue雇盖,我們通過這連個隊列我們不難看出是采用了生產(chǎn)消費者模式,結(jié)合線程池實現(xiàn)了低阻塞的運行栖忠。在大多數(shù)時候崔挖,每個緩存它們都只是訪問自己的雙端隊列,這樣的話極大地減少了競爭娃闲。當(dāng)工作者線程需要訪問另一個隊列時虚汛,它會從隊列的尾部而不是頭部獲取工作,因此進一步降低了隊列上的競爭程度皇帮。
Dispatcher的線程池ExecutorService
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
使用單利的方式創(chuàng)建線程池,那么我們來解釋一下線程池的幾個參數(shù)
1.int corePoolSize: 0 線程池的基本大小蛋辈,即在沒有任務(wù)需要執(zhí)行的時候線程池的大小属拾,并且只有在工作隊列滿了的情況下才會創(chuàng)建超出這個數(shù)量的線程将谊。
2.int maxmumPoolSize: Integer.MAX_VALUE 最大線程數(shù),就是當(dāng)前任務(wù)進行時渐白,此線程池能擴充的最大值尊浓。這里是無限大。
3.long keepAliveTime:60 當(dāng)前線程數(shù)大于核心線程數(shù)纯衍,成為空閑線程,當(dāng)空閑線程存活時間大于這個時間就會被取消
4.TimeUnit unit: TimeUnit.SECONDS 存活時間的單位是秒
5.BlockingQueue<Runnable> workQueue: new SynchronousQueue<Runnable>() 一個阻塞隊列,用來存儲等待執(zhí)行的任務(wù)
6.ThreadFactory threadFactory: 創(chuàng)建線程的工廠 Util.threadFactory("OkHttp Dispatcher", false)
前面我們分析了同步和一部的方法俺祠,就不做過多解釋了困鸥。
我們來看一下如何從ready到runing的添加的。
每次Call結(jié)束的時候都會調(diào)用finshed
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!");
//每次remove完后歌亲,執(zhí)行promoteCalls來輪轉(zhuǎn)菇用。
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
//線程池為空時,執(zhí)行回調(diào)
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
這個方法可以看出來是遍歷了readyAsyncCalls陷揪,把Call一一添加到了RunningAysncCalls惋鸥。
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) { i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return;
}
}
getResponseWithInterceptorChain()
這一步是OkHttp中最重要的一部,也是核心悍缠。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
代碼很簡單就是將
自定義的攔截器和OkHttp內(nèi)置的攔截器放到一個List集合中卦绣,然后把攔截器集合和Request一起創(chuàng)建了一個RealInterceptorChain對象,然后調(diào)用proceed方法把整個攔截器組合成鏈狀飞蚓。最終返回一個Response
責(zé)任鏈模式:一個請求沿著一條“鏈”傳遞滤港,直到該“鏈”上的某個處理者處理它為止。
- client.interceptors() :
自定義的攔截器 - retryAndFollowUpInterceptor:
失敗后重連或者服務(wù)器返回請求重新發(fā)起請求的攔截器玷坠。 - BridgeInterceptor:
鏈接客戶端代碼和網(wǎng)絡(luò)代碼的橋梁蜗搔,也就是說配置請求內(nèi)容(設(shè)置內(nèi)容長度,內(nèi)容編碼八堡,設(shè)置gzip壓縮樟凄,添加cookie,設(shè)置其他報頭) - CacheInterceptor
緩存機制攔截器兄渺,也就是說有沒有滿足請求的緩存有的話返回Cache缝龄。當(dāng)服務(wù)器返回數(shù)據(jù)有變化時更新Cache,如果當(dāng)前Cache失效就刪除挂谍。 - ConnectInterceptor:
建立服務(wù)器連接叔壤,正式開啟了網(wǎng)絡(luò)請求,調(diào)用連接池口叙,開啟Socket鏈接炼绘。 - networkInterceptors:
配置 OkHttpClient 時設(shè)置 - CallServerInterceptor
向服務(wù)器發(fā)送請求,并最終返回Response對象供客戶端使用妄田。
如何讓整個鏈狀攔截器運轉(zhuǎn)起來的俺亮?咱們現(xiàn)在看一下proceed方法
//正式開始調(diào)用攔截器工作
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection){
//省略部分與本文無關(guān)的代碼
// 調(diào)用鏈中的下一個攔截器
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
//確保每個攔截器都調(diào)用了proceed方法()
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
//省略部分與本文無關(guān)的代碼
return response;
}
其中有個index變量驮捍,每次調(diào)用都加1,然后獲得下一個攔截器脚曾,procced方法并沒有用for循環(huán)來遍歷interceptors集合东且,而是重新創(chuàng)建了一個RealInterceptorChain對象,且新對象的index在原來RealInterceptorChain對象index之上進行index+1,并把新的攔截器鏈對象RealInterceptorChain交給當(dāng)前攔截器Interceptor 的intercept方法本讥;查看BridgeInterceptor珊泳、CacheInterceptor等Okhttp內(nèi)置攔截器就可以印證這一點:在它們intercept的內(nèi)部都調(diào)用了chain.proceed()方法,且每次調(diào)用都在會創(chuàng)建一個RealInterceptorChain對象拷沸。所以整個攔截器的工作流程是這樣的:
那么到此為止一個完整的Okttp請求的流程就已經(jīng)完成色查。