接上篇同步请求分析,首先我们先看简单的异步请求的使用方法。
1. 异步请求使用
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("http://baidu.com") .build(); Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { } });
从上面的代码来看,和同步请求不同的部分就是最后是执行的call的enqueue方法,传入的是一个callback对象,用于回调请求的结果。
2. enqueue方法分析
我们还是进入Call的实现类RealCall来看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)); }
大部分代码与同步请求的无差别,主要看最后一行,它用我们传入的callback对象封装了一个AsyncCall对象,并传入dispatcher.
AsyncCall其实就是个Runnable,AsyncCall类中会重写NamedRunnable中的excute方法。
final class AsyncCall extends NamedRunnable { private final Callback responseCallback; AsyncCall(Callback responseCallback) { super("OkHttp %s", redactedUrl()); this.responseCallback = responseCallback; }
继续看dispatcher的enqueue方法。主要逻辑也比较简单:对runningAsyncCalls队列的size和最大请求数(5)进行判断,判断通过就将call入队,并调度线程池执行。否则就将请求加入readyAsyncCalls队列,在合适的时机再放入runningAsyncCalls队列
synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } }
3.线程池的创建
同样在Dispatcher类中,我们看一下线程池的创建过程
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; }
corePoolSize:0 设定为0可以在一段时间空闲后销毁所有线程
最大线程数量:integer的最大值(有同学可能会有疑问,最大线程数量是最大值,创建线程是否会对性能有严重影响?其实是不会的,Dispatcher类中已经对最大请求数做了限制 maxRequests = 64;)
SynchronousQueue:是一种没有容量的阻塞队列,每put一个元素后必须要等待一个take,比较适合做传递性设计
4.AsyncCall分析
上面提到了AsyncCall,他是继承自NamedRunnable,NamedRunnable又实现了Runnable接口,在run方法中,执行了execute方法,这个是一个抽象方法,会在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的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); } }
首先,这边使用拦截器链获取一个response,接着判断拦截器执行是否取消,取消则回调oFailure方法,否则得到response。同样要注意,这里回调的fail和response都是在子线程中,因为整个过程在独立线程中执行,并没有做线程切换。
再看finally的设计,调用了dispatcher的finished方法,这个方法在同步请求分析时提到过,移除了执行完毕的call,但是这里有个标志位和同步请求的不同
void finished(AsyncCall call) { finished(runningAsyncCalls, call, true); }
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(); } }
这里promoteCalls为ture,会调用promoteCalls方法,promoteCalls方法里会对Ready和running这两个队列的call进行调整,然后重新计算正在执行的线程数量,详细的会在Dispatcher中进行分析。
到这里,异步请求的细节基本分析完毕,下一节,我们一起来分析核心的Dispatcher