前言
关于okhttp的介绍和同步请求,请看上一篇文章,个人笔记---Okhttp源码跟踪之同步,这里就不在赘述了,直接进入正题。
异步请求
/**
* 异步请求
*/
public void asyncCall(Callback callback) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://gank.io/api/today")
.build();
client.newCall(request).enqueue(callback);
}
异步请求时多了一个callback回调,请求结果会在这个回调中返回。
前面创建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));
}
异步也需要判断当前call请求是否已经执行过,逻辑和同步也是一样的。然后又到了任务调度器的enqueue方法,并且传入了一个AsyncCall实例,AsyncCall是RealCall的内部类, 实现了Runnable接口,后续请求的时候会被放入线程池中,继续跟踪dispatcher的enqueue方法
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
首先会判断当前正在运行的异步队列的大小是否小于了64(默认支持最大并发送为64),单个host请求数是否小于5,满足两个条件,将请求加入runningAsyncCalls队列,并且将call请求放入线程池执行,如果任一条件不满足,则将请求放入readyAsyncCalls等待队列,等待队列的逻辑会放在最后分析
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;
}
如果线程池为空,则创建一个核心线程为0,最大线程为Integer.MAX_VALUE, 空闲线程存在的最大时间为60s的线程池,线程池创建完毕以后就开始执行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这个内部类在异步请求中还是比较重要的,它继承了一个叫NamedRunnable的抽象类,当线程运行时会调用execute这个抽象方法,因为AsyncCall是RealCall的内部类,所以这个抽象方法的具体实现是在RealCall里面,继续跟踪
@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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
通过一系列的拦截器去获取网络请求结果,过程和同步是同样的,关于拦截器的过程我会专门开一篇文章记录,这里暂时不详细说。
如果请求被取消则回调onFailure接口,未被取消则回调onResponse接口,我们可以看到回调的两个方法并没有切换线程,还是在子线程中,不能操作UI,当前请求执行完毕,调用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();
}
}
将执行完毕的请求从runningAsyncCalls队列中移除,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.
}
}
如果此时请求数超过maxRequest数量,则直接return;若readyAsyncCalls等待队列中没有请求,则直接return;遍历等待队列,如果此时单个host请求数量小于5,将此请求从等待队列移除并加入到runningAsyncCalls队列中,将请求放入线程池中去执行,执行结束也会调用promoteCalls这个方法,直到readyAsyncCalls队列为空,这样就完成了所有请求。
至此,整个异步请求的过程也分析完成,因为能力有限,分析的比较粗略,只是对整个请求的流程梳理一下,后续会再深究一下其中的细节。
因水平有限,如有错误请指正,不胜感激!