Anroid OKhttp笔记1 流程分析
Android OkhttpInterceptor 笔记:RetryAndFollowUpInterceptor
Android OkhttpInterceptor 笔记:BridgeInterceptor
Android OkhttpInterceptor 笔记:ConnectInterceptor
Android OkhttpInterceptor 笔记:CacheInterceptor
Android OkhttpInterceptor 笔记:CallServerInterceptor
Android Okhttp笔记:ConnectionPool
Android Okhttp3:Dispatcher分析笔记
一、概述
Dispatcher是异步请求的执行策略。在OkHttp中承担着对同步和异步请求的分发和回调.
1.1构造方法
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
1.2成员变量
private int maxRequests = 64;//最多并发请求的个数
private int maxRequestsPerHost = 5;//每个主机最大请求数
//执行请求的线程池
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<>();
//闲置接口,用来处理当dispatcher变为空闲状态(即所有的请求数为0)时的回调。
private @Nullable Runnable idleCallback;
1.3线程池
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;
}
在Call去调用到Dispatcher的对应的方法的时候才会去创建该实例,核心线程的个数为0,线程池的线程数的最大值为 Integer.MAX_VALUE,超时时间为60s,阻塞队列为同步队列。
二、分析
Dispatcher中共存有三个队列:
readyAsyncCalls 等待执行异步队列
runningAsyncCalls 正在执行异步队列
runningSyncCalls 正在执行同步队列
RealCall在执行execute/enqueue时,就是将这次请求加入到Dispatcher的三个集合中。而 Dispatcher通过管理队列集合元素对同步和异步请求进行分发和回调。
2.1同步请求执行过程
Call.execute()
继续调用 RealCall. execute()方法
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
//此处请求将进入Dispatcher对象内做相应的调度处理
client.dispatcher().executed(this);
//做真正的网络请求
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
client.dispatcher().executed(this):
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
将RealCall加入到runningSyncCalls集合中。这个Dispatcher管理的线程池并没有对同步请求分发进行管理,当处理完返回结果的时候,会调用Dispatcher的finish()来进行将该次的RealCall从队列集合中清空,然后在重新计算Dispatcher是否为空闲状态。
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
//将该call从集合中清空
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
//如果是true才会调用,也就是异步请求的时候才会调用
if (promoteCalls) promoteCalls();
//计算里面正在运行中请求的数量
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
//如果Dispatcher空闲,则回调idleCallback
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
2.2异步
异步请求call.enqueue();也是调用到了RealCall中的enqueue(),进入到源码中可以看到
@Override public void enqueue(Callback responseCallback) {
//.......省略代码
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
进入到Dispatcher的 enqueue()中可以看到在Dispatcher里面会去判断是否到达最大请求数,如果没有的话,直接将AsyncCall加入到runningAsyncCalls集合中,并执行该AsyncCall的run();否则就将AsyncCall加入到缓存队列readyAsyncCalls中。
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
2.3AsyncCall
AsyncCall extends NamedRunnable ,NamedRunnable 的run方法为:
@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实现了NamedRunnable 的execute();
@Override protected void execute() {
try {
//请求的过程,注意这里也是阻塞的
Response response = getResponseWithInterceptorChain();
//先不管这个Interceptor是干嘛的,下面的代码可以理解为:
//如果没有被取消,并且没有发生异常,回调onResponse方法。
//如果发生了异常或者被取消,回调onFailure方法。
if (retryAndFollowUpInterceptor.isCanceled()) {
//此请求被取消了,回调onFailure
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
//此请求成功了,回调onResponse
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
//发生了异常,回调onFailure
responseCallback.onFailure(RealCall.this, e);
} finally {
//通知Dispatcher Call被执行完毕了
client.dispatcher().finished(this);
}
}
我们可以看到同样通过 Response result = getResponseWithInterceptorChain();对发送同步请求和处理请求返回的结果。最后调用Dispatcher的finish()。但是和同步请求不同的是,此时传入的参数发生了变化
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, 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.
}
}
promoteCalls()方法就是试图去readyAsyncCalls中取出Call来加入runningAsyncCalls中执行。
Q/?为什么这么做?
同步调用结束因为并没有涉及到runningAsyncCalls中的任何东西,对runningAsyncCalls没任何影响,所以不需要调用promoteCalls。而异步的调用结束意味着runningAsyncCalls中会出现一个空位值,所以它会调用promoteCalls去尝试从readyAsyncCalls中拉一个进来。
三、总结
1)Dispatcher只是把同步请求放入了Dispatcher中的同步请求的缓存队列中,这个执行并不是由Dispatcher调度的。仅仅用来统计正在运行的请求个数。
2)Dispatcher会对异步请求进行缓存管理,并且有正在运行的异步请求执行完毕,只要没有超出最大链接数,就会自动从缓存中取出一个请求加入到正在运行的请求队列中,等待执行。
3)同步请求没有另开线程去执行,所以需要将代码放到子线程中去执行;而异步请求已经在子线程中执行了,不在需要开启子线程执行代码,所有异步执行的请求都会通过executorService线程池。
Q/?为什么不直接使用线程池来维护请求队列?
个人理解dispatch配合阻塞式线程池的优点:
1.不管是同步还是异步请求,在try/finally中调用了finished函数,是主动控制等待队列的移动,而不是采用锁或者wait/notify,操作更加简易,更容易控制队列。
2.使用的ArrayDeque队列,使用了可变数组实现,效率高于LinkedList。dispatch主动控制队列然后提交给线程池执行,线程池的核心线程数为0,最大线程数为Integer.MAX_VALUE,非核心线程的最大空闲时间为60秒, 任务队列用SynchronousQueue队列,这是一个无缓冲的阻塞队列,有任务到达的时候,只要没有空闲线程,就会创建一个新的线程来执行,这样的设计达到了高并发,低阻塞的运行。
@date 2020年4月6日 17:54:40
拖了很久的学习笔记总算填完坑了。
Anroid OKhttp笔记1 流程分析
Android OkhttpInterceptor 笔记:RetryAndFollowUpInterceptor
Android OkhttpInterceptor 笔记:BridgeInterceptor
Android OkhttpInterceptor 笔记:ConnectInterceptor
Android OkhttpInterceptor 笔记:CacheInterceptor
Android OkhttpInterceptor 笔记:CallServerInterceptor
Android Okhttp笔记:ConnectionPool
Android Okhttp3:Dispatcher分析笔记