OkHttp深入理解(1)綜述
阿新 • • 發佈:2019-02-16
用OkHttp這麼久,一直只知道基本用法, 沒有去深入探究,以至於遇到一些需求的時候不知道該如何實現,在網路請求部分中寫了許多冗餘程式碼,對於有程式碼潔癖的我來說簡直太痛苦了。現在查閱了許多資料,也慢慢看了一些原始碼,總算有了粗略的瞭解。
OkHttp的總體流程大致如下:
首先通過OkHttpClient.Builder建立一個client物件,然後通過Request.Builder構建一個Request物件,並用client.newCall(request)方法建立一個call出來。然後通過call.execute()進行同步請求,或者通過call.enqueue(Callback)進行非同步請求。 同步請求 @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
{
eventListener.callFailed(RealCall.this,
e);
responseCallback.onFailure(RealCall.this,
e);
}
}
finally
{
client.dispatcher().finished(this);
}
}
}
上面這段程式碼是進行同步請求的程式碼,可以看到try程式碼塊中第一行呼叫了getResponseWithInterceptorChain方法來獲取Response。在這個方法裡,會依次新增自定義的Interceptor、RetryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、ConnectInterceptor、自定義的networkInterceptor、CallServerInterceptor。並通過構建Chain來進行鏈式呼叫。程式碼如下:
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,
this,
eventListener,
client.connectTimeoutMillis(),
client.readTimeoutMillis(),
client.writeTimeoutMillis());
return
chain.proceed(originalRequest);
}
最後一行中返回了chain.proceed(request),這個方法會返回一個Response物件。在這個方法內部會通過自增遞迴方式依次呼叫完鏈中的每一個Interceptor,程式碼較多,擷取一部分如下:
// Call the next interceptor in the chain.
RealInterceptorChain next =
new
RealInterceptorChain(interceptors,
streamAllocation, httpCodec,
connection,
index
+
1, request,
call,
eventListener,
connectTimeout,
readTimeout,
writeTimeout);
Interceptor interceptor =
interceptors.get(index);
Response response = interceptor.intercept(next);
在chain.proceed(Request)方法內部,會先將Interceptor陣列的下標index+1,創建出鏈中的下一個需要被呼叫的interceptor的鏈節點next。然後呼叫當前這個節點的interceptor的intercept(Chain)方法,將下一個鏈節點next傳進去,下一個鏈節點next的proceed方法將會在當前這個節點的intercept(Chain)方法中被呼叫。通過這樣的流程,完成對鏈中每一個Interceptor的呼叫,並在獲取到Response之後,沿原路依次返回,並依次被各個Interceptor處理,最後返回到最初呼叫getResponseWithInterceptorChain()處。
在這整個過程中採用了責任鏈模式進行處理,責任鏈模式將每個處理者(在這裡即是Interceptor)連成一條鏈,每個節點持有下一個節點的引用(每次呼叫Interceptor.intercept(Chain)方法時,都會接收到傳入的下一個節點Chain物件)。請求從鏈頭開始沿著鏈依次傳遞,直到有一個處理者能處理為止(在這裡是鏈上的最後一個節點CallServerInterceptor進行最終的請求發起並返回Response)。這裡採用責任鏈模式的好處在於將請求與處理者之間解耦,發出請求的客戶端並不需要知道到底是鏈上哪一個處理者對請求進行了處理,使得我們可以在不影響客戶端的情況下動態地重新組織和分配責任(新增或移除Interceptor)。
非同步請求
通過呼叫call.enqueue(Callback)方法進行非同步請求,在enqueue方法中最終通過呼叫call中持有的client物件的client.dispatcher().enqueue(new AsyncCall(Callback))方法進入請求佇列。
Dispatcher是作為一個非同步請求分發策略的存在。裡面維護瞭如下幾個變數:
①、maxRequests = 64:最大併發請求數為64。
②、maxRequestsPerHost = 5:每個主機最大請求數為5.
③、Dispatcher:分發者,也就是生產者
④、AsyncCall:一個Runnable物件,裡面封裝了非同步回撥介面
⑤、ExecutorService:執行緒池,也就是消費者池。
⑥、Deque<AsyncCall> readyAsyncCalls:就緒狀態的非同步calls佇列。
⑦、Deque<AsyncCall> runningAsyncCalls:正在執行的非同步calls佇列,包含了已經被canceled,但還沒有finished的calls
⑧、Deque<RealCall> runningSyncCalls:正在執行的同步calls佇列,包含了已經被canceled,但還沒有finished的calls,之所以儲存起來是為了方便進行統計正在執行的calls數目,同時在需要時統一對所有的calls執行canceled。
回到非同步請求原始碼,在Dispatcher的enqueue方法裡,會判斷當前正在執行的非同步請求數目是否小於maxRequests 且 當前每個Host的非同步請求數是否小於maxRequestsPerHost,如果兩個條件都滿足,則把call加入到runningAsyncCalls佇列中,並呼叫執行緒池進行執行call;否則把call新增進readyAsyncCalls佇列中,程式碼如下:
synchronized void
enqueue(AsyncCall call) {
if
(runningAsyncCalls.size()
<
maxRequests
&& runningCallsForHost(call) <
maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
}
else
{
readyAsyncCalls.add(call);
}
}
以上便是OkHttp的大致執行流程。關於非同步請求線上程池中是如何呼叫的,將在ExecutorService相關筆記中記錄;關於各個攔截器的功能與工作流程、路由定址、鏈路複用等,將在另外的獨立筆記中記錄。
首先通過OkHttpClient.Builder建立一個client物件,然後通過Request.Builder構建一個Request物件,並用client.newCall(request)方法建立一個call出來。然後通過call.execute()進行同步請求,或者通過call.enqueue(Callback)進行非同步請求。 同步請求 @Override protected void execute() { boolean signalledCallback = false; try { Response response = getResponseWithInterceptorChain();