如果要说OkHttp中的大头,Dispatcher
肯定是占据一席之地的。在OkHttp中,Dispatcher
负责任务的分发和调度。今天来重点的说说它。
其实这个类非常的简单,大家不要将它想的太难了。
1. 同步请求的Dispatcher
同步请求是通过Call
的execute
方法来执行的,虽然我们在分析同步请求时,已经看过这个方法了,但是在这里我们还是再来看一遍,这一次我们只关心Dispatcher
相关的操作:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
我们从这段代码可以看出来,在调用getResponseWithInterceptorChain
方法之前,我们首先Dispatcher
的executed
方法,我们来看看在这个方法是怎么进行操作.
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
同步请求的操作非常简单,直接将当前这个Call
添加到runningSyncCalls
队列,就没有做其他的事情了。
同时在execute
方法的最后,还调用了Dispatcher
的finished
方法。我猜,肯定是将当前的Call
对象从队列中移除。
同步请求的Dispatcher
过程就是这么简单。接下来,我们来看看异步请求的Dispatcher
过程。
2. 异步请求的Dispatcher
异步请求是通过Call
的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));
}
异步执行的核心在enqueue
方法最后一行。我们来看看Dispatcher
的enqueue
方法:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
这个方法在上一篇文章中已经分析过了,这这里就不在做过多的解释,这里我们需要注意的一点是:当不满足if条件时,会将当前的Call
对象放在一个等待队列中,但是什么时候将当前这个Call
从等待队列移除呢?这个就是Dispatcher
的职责了。关于这个问题,我们需要从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);
}
}
}
我们重点关注如下代码:
client.dispatcher().finished(this);
如上的代码最终会执行到Dispatcher
的finished
方法中去:
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();
}
}
而在调用finished
时,promoteCalls
参数我们传入的是true
,所以会调用promoteCalls
方法,最后,我们可以这个方法里面来找到答案:
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; // Reached max capacity.
}
}
从上面的代码中,我们可以看出来,当当前的执行队列不是满的的话,就会从等待队列去取出相应的Call放入执行队列中去,并且提交到线程池去执行。
这就是异步请求的任务调度过程,是不是也是非常的简单?
3.总结
同步请求的调度过程是非常简单的,这里就不对同步做总结,而对异步请求做一个简单的总结。
1. 异步请求通过调用Call
的enqueue
方法将一个Call
对象放入到一个队列当中,如果当前的执行队列没有满的话,就放在执行队列中,立即执行;如果满了的话,就放在等待队列中去。
2.异步请求,当一个任务执行完毕之后,会调用Dispatcher
的finished
方法。在finished
方法里面,会调用promoteCalls
方法会重新调整队列。
网友评论