Dispatcher封装了管理、处理call的策略,以及实现了线程池。
1. 请求管理策略
1.1 请求队列分类
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
...
}
Dispatcher内部维护了3个请求队列,都是双端队列Deque, 可以从执行策略与请求类型两个维度来划分:
1.1.1 按执行策略分类
进行队列:runningAsyncCalls + runningSyncCalls
等待队列:readyAsyncCalls
// 等待队列中请求数量
public synchronized int queuedCallsCount() {
return readyAsyncCalls.size();
}
// 进行队列中请求数量 (正在处理的:同步请求 + 异步请求)
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
1.1.2 按请求类型分类
异步请求队列:runningAsyncCalls + readyAsyncCalls
同步请求队列:runningSyncCalls
// 添加到异步请求队列
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
// 添加到同步请求队列
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
- 异步请求,进入Dispatcher流程,来管理、分发请求,并调用线程池执行请求;
- 同步请求,进入Dispatcher流程仅仅是为了计算1.1.1中的runningCallsCount(),
其执行请求的过程是直接在RealCall#execute()方法中触发的。
1.2.异步请求与同步请求
1.2.1 RealCall发起异步请求
RealCall#enqueue(Callback responseCallback)
@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));
}
异步请求,会将call
- 添加到异步请求队列中
- Dispatcher会根据情况添加到“进行”与“等待”两个队列中,并在线程池中执行请求
- (进入线程池)具体执行是RealCall.AsyncCall#execute()方法,内部调用getResponseWithInterceptorChain()返回响应结果Response
- 通知Dispatcher,当前请求finish
Dispatcher#enqueue
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
Dispatcher#enqueue流程,管理、分发请求,触发线程池执行请求。
1.2.2 RealCall发起同步请求
RealCall#execute()
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
// 添加到同步请求队列runningSyncCalls
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);
}
}
同步请求是直接在RealCall#execute()方法中执行的,运行在当前线程(发起调用的线程),没有callback,直接返回响应结果。
getResponseWithInterceptorChain()方法的具体实现,在下一篇文章中讲解。
- 添加到同步请求队列
- 直接调用getResponseWithInterceptorChain()返回响应结果
- 调用Dispatcher#finish, 触发下一个请求
Dispatcher#executed
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
2. 线程池定义
// 懒加载:用时才创建
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;
}
线程池仅仅对异步请求有作用,所以采用懒加载的方式创建线程池。
在仅有同步请求的情况下,避免创建无用对象。
3.总结
- 管理请求队列
维护3个Deque队列,管理同步、异步请求(ArrayDeque内部使用循环数组来实现Stack和Queue的功能,效率高,推荐使用,非线程安全) - 分发请求
异步请求是RealCall交给Dispatcher管理,在Dispatcher#enqueue() 方法中发起,提交给线程池执行;
同步请求直接在RealCall#execute() 方法中发起,当前线程中执行。 - 线程池
线程池仅仅针对异步请求有作用,注意区分Dispatcher对两种请求的不同作用。
异步请求:Dispatcher来管理队列、分发请求到线程池执行;
同步请求:Dispatcher仅仅是管理队列,分发请求的过程在RealCall中。