系列文章:
- okhttp3 源码解析笔记整理——同步、异步请求
- okhttp3 源码解析笔记整理——拦截器与请求的实现
- okhttp3 读书笔记——默认的线程池,addNetworkInterceptor() 与 addInterceptor() 的区别,复用连接池
额外参考文章:
【Android】OkHttp源码分析/#CallServerInterceptor
1、同步请求
okhttp 可以使用 RealCall # execute()
方法来进行一个同步的请求。
try {
Response response = mOkHttpClient.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
RealCall # execute()
方法如下:
@Override public Response execute() throws IOException {
// 限制 call 只能执行一次请求
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
// 使用超时机制
timeout.enter();
eventListener.callStart(this);
try {
// 利用调度器 dispatcher 将当前请求添加到一个收集正在运行的同步请求的队列 runningSyncCalls 中
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 {
// 最后,将当前请求从 runningSyncCalls 中移除
client.dispatcher().finished(this);
}
}
在上述方法中,涉及到两个重点的点。
第一个就是调度去 Dispatcher
,其内部的成员变量如下:
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<>();
...
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
}
其中就有 runningSyncCalls
,用于收集正在运行的同步请求。而 readyAsyncCalls
用于收集准备运行的异步请求,runningAsyncCalls
用于收集正在运行的异步请求。
executorService
为异步请求时要用到的线程池。executorService()
方法中表明了其的默认实现。
第二个是 getResponseWithInterceptorChain()
方法,在方法中,会通过构造一系列的拦截器,来组成一个拦截器链来实现真实的请求。
2、异步请求
okhttp 实现异步请求:
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) {
}
});
RealCall # enqueue()
@Override public void enqueue(Callback responseCallback) {
// 限制 call 只能执行一次请求
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
// 添加进 dispatcher 的 readyAsyncCalls 中
// 会把 responseCallback 包装成 AsyncCall
//(AsyncCall是 RealCall 的内部类,实现了 NamedRunnable)
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
Dispatcher
void enqueue(AsyncCall call) {
synchronized (this) {
// 先将包装成 AsyncCall 的 call 添加进 readyAsyncCalls
readyAsyncCalls.add(call);
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.get().forWebSocket) {
// 如果不是 WebSocket,则在 runningAsyncCalls 中和 readyAsyncCalls 中寻找是否有连接相同主机的请求 existingCall
AsyncCall existingCall = findExistingCallWithHost(call.host());
// 如果有找到,则将 existingCall 的 callsPerHost 引用赋值给当前 call 的 callsPerHost
// 这样做的目的是在于,对于异步请求,okhttp 会对有着相同主机的 call 在请求时进行计数,
// 默认限制能够同时请求同一主机的 call 的最大个数为 Dispatcher # maxRequestsPerHost(5)
// 而实现计数的关键就在 callsPerHost,它是 AtomicInteger 的实例对象
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
// 调用 promoteAndExecute() 方法来推动和执行存放在 readyAsyncCalls 中的异步请求
promoteAndExecute();
}
@Nullable private AsyncCall findExistingCallWithHost(String host) {
for (AsyncCall existingCall : runningAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
for (AsyncCall existingCall : readyAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
return null;
}
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
// 从 readyAsyncCalls 中取出可执行的请求先存放到临时的 executableCalls 中,
// 以及转移到 runningAsyncCalls 中
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
// 如果正在运行的请求达到了最大值,则先退出
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
// 如果请求 asyncCall 对应的主机的连接数达到了最大值,则先跳过该请求
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
// 将 asyncCall 的 callsPerHost 自增 1,callsPerHost 的作用在上面讲过
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
// 执行 executableCalls 中存放的请求
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
// 通过 executorService 线程池来执行 asyncCall 请求
asyncCall.executeOn(executorService());
}
return isRunning;
}
可以看到,对于一个异步请求,会包装成 AsyncCall
,然后通过 Dispatcher
中的线程池 executorService
来执行。
AsyncCall
继承自 NamedRunnable
,NamedRunnable
则实现了 Runnable
。 而在 NamedRunnable#run()
方法中,会执行 NamedRunnable#execute()
这一抽象方法。AsyncCall
则实现 execute()
方法。
AsyncCall # executeOn()
/**
* Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
* if the executor has been shut down by reporting the call as failed.
*/
void executeOn(ExecutorService executorService) {
...
// 通过线程池来执行当前 realCall,因为 realCall 本质上就是 Runnable
// 这样,就切换到了子线程中,从而实现了异步
executorService.execute(this);
...
}
线程池 executorService
的默认实现在前面有说(即通过 Dispatcher # executorService()
方法)。
因此,executorService.execute(this)
实际上会触发 AsyncCall
的 execute()
方法(因为其父类 NamedRunnable#run()
的缘故)
@Override protected void execute() {
boolean signalledCallback = false;
// 用于监测超时的
timeout.enter();
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) {
e = timeoutExit(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);
}
}
可以看到,尽管是异步请求,但是最终也是通过 getResponseWithInterceptorChain()
来实现请求的。
有关 getResponseWithInterceptorChain()
可以参见:okhttp 源码解析笔记整理——拦截器与请求的实现
另外,对于 readyAsyncCalls
也许会有一个疑问,那就是怎么保证该队列中的请求都能执行完呢?
这里可以追溯源码,根据前面的内容可以直到,Dispather # promoteAndExecute()
方法会推动 readyAsyncCalls
中的请求的执行(使其转移到 runningAsyncCalls
中)。
而 Dispather # promoteAndExecute()
方法会在多个地方被调用,其中就有在 Dispather # finished()
中被调用:
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
而该方法又有在 Dispather
的两个重载方法中被调用:
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
call.callsPerHost().decrementAndGet();
finished(runningAsyncCalls, call);
}
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call);
}
而第一个重载方法会在 RealCall # executeOn()
和 RealCall.Async # execute()
中被调用;
第二个重载方法会在 RealCall # execute()
被调用。
这样,就可以保证 Dispather # promoteAndExecute()
方法重复不断的被触发,最终保证 readyAsyncCalls
中的请求都被执行。