OkHttp源碼解讀總結(四)—>OkHttp異步請求源碼總結
標籤(空格分隔): OkHttp源碼 學習筆記
前言
- 以下的相關知識總結是通過慕課網的相關學習和自己的相關看法,如果有需要的可以去查看一下慕課網的相關教學,感覺還可以。
上一節已經總結了同步請求源碼,接下來查看異步,因爲同步和異步的差別只在於Call之後的execute()和enqueue()方法,因此前三步驟這裏不再贅述。
enqueue()異步請求
call是個接口,因此enqueue這個方法要到實現類RealCall中進行
@Override
public void enqueue(Callback responseCallback) {
//鎖
synchronized (this) {
//是否已經執行過
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//捕捉堆棧信息
captureCallStackTrace();
//開啓監聽
eventListener.callStart(this);
//通過將傳入的Callback對象傳入AsyncCall()裏面創建一個AsyncCall對象實例 因此當我們通過client.dispatcher獲取到Dispatcher這個分發器 然後傳入到剛創建好的Runnable這個實例,然後調用enqueue()方法進行異步網絡請求
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
client.dispatcher()
//返回Dispatcher對象
public Dispatcher dispatcher() {
return dispatcher; //這個就是在okhttpclient的構造方法的第一行就已經實例化好的
}
AsyncCall(responseCallback)
class AsyncCall extends NamedRunnable //我們可以看出,這個AsyncCall繼承於NamedRunnable,查看NamedRunnable實現Runnable,因此AsyncCall就是一個Runnable實例
enqueue(AsyncCall call)
//同步鎖 傳入創建好的異步Runnable
synchronized void enqueue(AsyncCall call) {
//判斷實際的運行請求數是否小於允許的最大的請求數量(64) 並且共享主機的正在運行的調用的數量小於同時最大的相同Host的請求數(5)
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//如果都符合 那麼就把這個請求添加到正在執行的異步請求隊列當中
runningAsyncCalls.add(call);
//然後通過線程池去執行這個請求call
executorService().execute(call);
} else {
//否則的話 在就緒(等待)異步請求隊列當中添加
readyAsyncCalls.add(call);
}
}
executorService()(Dispatcher類中的方法)
//單例 返回ExecutorService這個線程池對象 同步
public synchronized ExecutorService executorService() {
if (executorService == null) {
//雖然這個地方定義了最大的線程池的數量是Integer.MAX_VALUE 但是我們知道上面對請求數量有了限制(64個)
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
關於線程池的推薦
深入理解java線程池—ThreadPoolExecutor
當創建好ExecutorService這個線程池對象之後,就需要調用execute(call)執行方法執行Runnable,也就是執行AsyncCall裏面的run()方法,我們去查找AsyncCall裏面的run方法,可以看到沒有這個方法,卻有一個execute()方法,而這個方法是複寫的父類的方法,我們查看NamedRunnable這個類,可以發現有個run方法
@Override public final void run() {
//獲取到當先線程的名字()
String oldName = Thread.currentThread().getName();
//賦值
Thread.currentThread().setName(name);
try {
//執行 這個是抽象類 也就是剛纔在AsyncCall看到的execute()方法
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
RealCall中的execute()方法
@Override protected void execute() {
boolean signalledCallback = false;
try {
//攔截器鏈
Response response = getResponseWithInterceptorChain();
//重定向/重試是否取消
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
//callback的onFailure()返回 在call.enqueue()裏面傳進去的
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);
}
}
finished(AsyncCall call)方法
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
finished(runningAsyncCalls, call, true)方法
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
//1、調用calls.remove()方法進行刪除正在請求的異步線程
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
//2、調用promoteCalls()方法調整整個請求隊列
if (promoteCalls) promoteCalls();
//3、重新計算正在執行的線程的數量
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
最終不管是成功還是失敗,都會返回到我們複寫的Callback方法裏面
requestCall.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//當請求出錯的時候 會回調到這個方法中
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//要注意這個地方是子線程,okhttp並沒有給我們把響應的數據轉換到主線程中,因此我們在這個地方更新UI的時候
//是會報錯的 需要我們手動的轉換到主線程 然後才進行數據的解析和UI更新 在之前的retrofit的講解中
//也稍微有所涉及 那就是retrofit已經幫我們把異步的這個請求 返回來的callback已經切換到了主線程
//得益於retrofit裏面的Executorer這個回調執行器
if (response.isSuccessful()){
String string = response.body().string();
}
}
});
總結
- 1、判斷當前call
- 這個請求只能被執行一次,如果已經請求過了,就會拋出異常
- 2、通過傳遞進來的Callback封裝成一個AsyncCall(Runnable)對象
- 3、獲取Dispatcher對象並執行enqueue()異步請求
- 如果這個AsyncCall請求符合條件(判斷實際的運行請求數是否小於允許的最大的請求數量(64) 並且共享主機的正在運行的調用的數量小於同時最大的相同Host的請求數(5)) 纔會添加到執行異步請求隊列,然後通過線程池進行異步請求
- 否則就把這個AsyncCall請求添加到就緒(等待)異步請求隊列當中
- 這個Dispatcher持有一個正在執行的請求隊列/就緒(等待)執行的請求隊列,還有一個就是線程池。這三個來完整的處理異步請求操作。