接上篇同步請求分析,首先我們先看簡單的異步請求的使用方法。
1. 異步請求使用
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://baidu.com")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
從上面的代碼來看,和同步請求不同的部分就是最後是執行的call的enqueue方法,傳入的是一個callback對象,用於回調請求的結果。
2. enqueue方法分析
我們還是進入Call的實現類RealCall來看enqueue的具體實現
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
大部分代碼與同步請求的無差別,主要看最後一行,它用我們傳入的callback對象封裝了一個AsyncCall對象,並傳入dispatcher.
AsyncCall其實就是個Runnable,AsyncCall類中會重寫NamedRunnable中的excute方法。
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
繼續看dispatcher的enqueue方法。主要邏輯也比較簡單:對runningAsyncCalls隊列的size和最大請求數(5)進行判斷,判斷通過就將call入隊,並調度線程池執行。否則就將請求加入readyAsyncCalls隊列,在合適的時機再放入runningAsyncCalls隊列
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
3.線程池的創建
同樣在Dispatcher類中,我們看一下線程池的創建過程
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;
}
corePoolSize:0 設定爲0可以在一段時間空閒後銷燬所有線程
最大線程數量:integer的最大值(有同學可能會有疑問,最大線程數量是最大值,創建線程是否會對性能有嚴重影響?其實是不會的,Dispatcher類中已經對最大請求數做了限制 maxRequests = 64;)
SynchronousQueue:是一種沒有容量的阻塞隊列,每put一個元素後必須要等待一個take,比較適合做傳遞性設計
4.AsyncCall分析
上面提到了AsyncCall,他是繼承自NamedRunnable,NamedRunnable又實現了Runnable接口,在run方法中,執行了execute方法,這個是一個抽象方法,會在AsyncCall進行重寫
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
首先,這邊使用攔截器鏈獲取一個response,接着判斷攔截器執行是否取消,取消則回調oFailure方法,否則得到response。同樣要注意,這裏回調的fail和response都是在子線程中,因爲整個過程在獨立線程中執行,並沒有做線程切換。
再看finally的設計,調用了dispatcher的finished方法,這個方法在同步請求分析時提到過,移除了執行完畢的call,但是這裏有個標誌位和同步請求的不同
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
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();
}
}
這裏promoteCalls爲ture,會調用promoteCalls方法,promoteCalls方法裏會對Ready和running這兩個隊列的call進行調整,然後重新計算正在執行的線程數量,詳細的會在Dispatcher中進行分析。
到這裏,異步請求的細節基本分析完畢,下一節,我們一起來分析核心的Dispatcher