Okhttp学习笔记
学习资料:
上一篇文章:
Okhttp学习笔记(一) 初步认识&& 源码浅析
Dispatcher类分析
- 发送的同步、异步请求由Dispatcher管理状态
- 作用:维护同步和异步的请求状态,维护一个线程池,用于执行请求
成员变量
// 最大请求数
private int maxRequests = 64;
// 相同主机最大请求数
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
// 重要 维护的线程池
private @Nullable ExecutorService executorService;
// 准备状态中的异步请求队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
// 执行中的异步请求队列(包含已经取消但是还没取消的请求)
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
// 同步请求队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
创建线程池
核心线程池设置为0,最大线程数设置为Integer的最大值,但是实际上受到maxRequests的限制,并且在空闲60s会自动回收线程;
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;
}
Dispatcher的executed方法
直接加入队列
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
Dispatcher的enqueue方法
synchronized void enqueue(AsyncCall call) {
// 判断请求数是佛满足要求
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
// 加入运行队列并且使用线程池执行
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
// 加入就绪队列
readyAsyncCalls.add(call);
}
}
在哪调用了?
以异步请求为例,在实际使用中,我们是通过OkHttpClient的newCall()方法调用的enqueue方法,在Okhttp学习笔记(一)提到过,实际执行的是RealCall类的enqueue方法
// realCall类里调用的dispatcher对象的 enqueue 方法
client.dispatcher().enqueue(new AsyncCall(responseCallback));
AsyncCall是一个包装了Callback的Runnable,是RealCall类的内部类,最终线程池会执行AsyncCall的execute方法,通过 getResponseWithInterceptorChain()方法执行请求获得请求结果,并且回调相应的结果,并且会通过RealCall持有的client客户端对象,最终调用Dispatcher的finished方法
final class AsyncCall extends NamedRunnable {
...
@Override protected void execute() {
boolean signalledCallback = false;
try {
// 获取拦截器链 获取response
Response response = getResponseWithInterceptorChain();
// 判断是否取消
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
// 取消则回调onFailure
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
// 没取消则回调onResponse
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 {
// 捕获到异常 回调到onFailure方法
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
// 调用finished方法
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;
}
// 执行数为0时,且idleCallback 不为空,执行idleCallback.run();
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
除了移除请求,重点看一下异步会执行的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); // 最终会执行AsyncCall的execute方法,见上面
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
总结:
- Dispatcher是一个调度器,在创建OkHttpClient自动创建;
- 默认最大请求数为64;
- 默认最大相同主机请求数为5;
- 同步请求: (在RealCall中)在请求前调用dispatcher加入请求队列,然后直接获取结果;
- 异步请求:在RealCall调用dispatcher的enqueue方法,在dispatcher的enqueue方法方法中判断加入请求队列或者加入等待队列,最终执行AsyncCall 的execute方法,获取请求结果并回调
- 请求完成:会调用Dispatcher的finished方法,移除请求,调整请求数,如果是异步请求的话还会调用promoteCalls方法来调用下一个请求
- 异步请求完成:promoteCalls方法中会判断是否满足两个条件(最大请求数和相同主机数),如果满足的话会按队列调用下一个请求