首先看Okhttp发起一个异步的请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void testOkHttp() {

OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); //构造 OkHttpClient
Request request = new Request.Builder()
.get() //Method GET
.url("www.baidu.com")
.build(); //构造请求信息

okHttpClient.newCall(request)
.enqueue(new Callback() { //发起异步请求
@Override
public void onResponse(final Call call, final Response response) throws IOException {
//成功拿到响应
int code = response.code();
ResponseBody body = response.body();
String string = body.string();
}

@Override
public void onFailure(final Call call, final IOException e) {
e.printStackTrace();
}
});
}

我们看下newCall方法,

1
2
3
@Override public Call newCall(Request request) {
return new RealCall(this, request);
}

newCall(Request) 方法调用了 RealCall.newRealCall() 方法,这个 RealCall.newRealCall() 方法创建了一个新的RealCall对象,这个RealCallokhttp3.Call` 接口的一个实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface Call extends Cloneable {
//返回这个请求关联的 Request 对象
Request request();

//立即执行请求,阻塞等待拿到响应
Response execute() throws IOException;

//请求入队,异步执行
void enqueue(Callback responseCallback);

//取消一个请求
void cancel();

boolean isExecuted();

boolean isCanceled();

Call clone();

interface Factory {
Call newCall(Request request);
}
}

可以看到,我们前面发起异步请求的 enqueue() 方法是定义在 Call 中的。

1
2
okHttpClient.newCall(request)
.enqueue(new Callback() { ...}); //原来就是 Call 的方法

在 OkHttp 中,Call 的唯一实现就是 RealCall,它表示一个准备好被执行的请求。和 Request 不同在于,它还提供了发起请求、取消等方法。

拿到 OkHttp.Call 的实例、RealCall 对象后,我们调用了它的 enqueue() 方法:

1
2
3
4
5
6
7
8
9
10
//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));
}

核心就在最后这句 client.dispatcher().enqueue(new AsyncCall(responseCallback));,它做了两件事:

  1. 创建一个 AsyncCall 对象
  2. 调用 Dispatcher.enqueue() 方法将请求入队

先看下 AsyncCall 是何方神圣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;

AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}

String host() { //用于标识这个请求
return originalRequest.url().host();
}

Request request() {
return originalRequest;
}

RealCall get() {
return RealCall.this;
}

@Override protected void execute() {
//...
}
}

可以看到, AsyncCall 就是一个 Runnable,用于异步执行任务。

接着看 client.dispatcher() 方法,它返回一个调度器 Dispatcher,这是 OkHttp 中比较核心的一个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public final class Dispatcher {
private int maxRequests = 64; //同时最多发起 64 个请求
private int maxRequestsPerHost = 5; //同一 host 最多发起 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<>();

public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}

public 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;
}
//...
}
  • 在一个 OkHttpClient 中一般只有一个 Dispatcher,因此一个 OkHttpClient 能发起的最多请求就是 Dispatcher 中定义的 64 个
  • 同样,同一 host 能发起的最多请求是 5 个
  • Dispatcher 中用三个队列保存同步、异步请求
  • 默认线程池核心线程数量为 0,最多数量不限制,消息队列为 SynchronousQueue,因此有请求时会不断创建新线程

接着我们看一下NamedRunnable 的execute在AsyncCall的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain(forWebSocket);
if (canceled) {
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!
logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}

可以看到,这个方法中先通过 getResponseWithInterceptorChain() 方法拿到了响应 Response,然后进行了回调,最后在 finally 代码块中调用了 client.dispatcher().finished(this) 方法,这个方法的作用是从调度器 Dispatcher 里移除当前这个请求,执行下一个。

我们重点看 getResponseWithInterceptorChain() 方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));

Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());

return chain.proceed(originalRequest);
}
小结

至此我们对 OkHttp 如何发起请求、逐层处理、拿到响应有了一个基本的认识,三句话概括一下:

  1. 发起异步请求后会构造异步任务 AsyncCall 入队,等被执行时会调用它的 execute() 方法
  2. 这个执行方法会通过拦截器链,挨个调用我们自定义的和系统内置的 5 个拦截器,对请求信息和响应做处理,最后返回结果,回调我们传入的参数
  3. 然后从队列中移除当前任务,执行下一个,以此循环

本文地址: http://www.yppcat.top/2020/07/27/Okhttp源码分析/