OkHttp 调用流程分析
一、概述
OkHttp 版本: 3.14.7
官方文档: OkHttp官方文档
Github: OkHttp
OkHttp 是默认情况下高效的HTTP客户端:
- HTTP/2 支持允许对同一主机的所有请求共享一个套接字。
- 连接池减少了请求延迟(如果HTTP/2不可用)。
- 透明GZIP缩小了下载大小。
- 响应缓存完全避免网络重复请求。
二、OkHttp 的简单使用
这部分详细的使用可以参考官方文档 OkHttp - Recipes 。
OkHttp 提供了同步和异步的请求方法,下面我们介绍一下常用的 Get
、Post
请求的使用方法。
1. Get 同步请求
private final OkHttpClient client = new OkHttpClient();
public void get() {
Request request = new Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build();
Response response = client.newCall(request).execute()
if (response.isSuccessful()) {
System.out.println(response.body().string());
}
}
2. Get 异步请求
private final OkHttpClient client = new OkHttpClient();
public void asynGet() {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
System.out.println(response.body().string());
}
}
});
}
3. Post 同步请求
MediaType JSON = MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute()
if (response.isSuccessful()) {
System.out.println(response.body().string());
}
}
三、OkHttp 源码分析
下面主要介绍一下以下几个类:
OkHttpClient
Dispatcher
RealCall
- 拦截器的调用:
getResponseWithInterceptorChain()
1. OkHttpClient 类
- OkHttpClient 类相当于一个门面类 (外观模式),可以配置很多参数,而 OkHttpClient 是通过 Builder 模式构建的。
- OkHttpClient 中可以配置很多参数,我们常用的几种如下面代码所示。
OkHttpClient.newCall()
返回的对象 Call 可以操作 任务的执行 和 任务的取消。
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher; //任务的调度器
this.proxy = builder.proxy;
this.protocols = builder.protocols; //支持的网络协议(Http1、Http2)
this.interceptors = Util.immutableList(builder.interceptors); //自定义的拦截器
this.networkInterceptors = Util.immutableList(builder.networkInterceptors); //自定义的拦截器
this.cache = builder.cache; //自定义的缓存
this.dns = builder.dns; //dns
this.callTimeout = builder.callTimeout;
this.connectTimeout = builder.connectTimeout; //连接超时
this.readTimeout = builder.readTimeout; //超时时间
this.writeTimeout = builder.writeTimeout; //超时时间
this.pingInterval = builder.pingInterval;
// ...代码省略...
}
2. Dispatcher 类
在介绍后面的执行流程前,先介绍以下 Dispatcher
类中的几个变量。
// Dispatcher.class
private int maxRequests = 64; //同时执行的任务数
private int maxRequestsPerHost = 5; //每个主机最大请求数为5
/** 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 int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
3. RealCall 类
RealCall
是 Call
的实现类,Call
接口定义了请求的 执行、取消 等相关操作。
RealCall 对象的构建
// OkHttpClient.class
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
// RealCall.class
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.transmitter = new Transmitter(client, call); //这里构造了一个Transmitter对象
return call; //可以操作请求的执行和取消。
}
同步执行任务
// RealCall.class
// 这里是同步执行任务
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();
transmitter.callStart();
try {
//步骤1:只是将当前同步执行的任务加入队列(不做其他处理)。
client.dispatcher().executed(this);
//步骤2:步骤1只将任务加入队列,不做任何处理,真正的请求是这一步执行的。
return getResponseWithInterceptorChain();
} finally {
//步骤3:步骤2任务完成后,需要将步骤1中加入同步队列的任务移除。
client.dispatcher().finished(this);
}
}
// Dispatcher.class
synchronized void executed(RealCall call) {
//因为OkHttp会控制当前最大允许执行的任务数,把这个同步任务加入队列,是便于后面控制并发数。
runningSyncCalls.add(call);
}
// Dispatcher.class
// 将同步请求的call从同步队列移除
void finished(RealCall call) {
finished(runningSyncCalls, call); //这里的代码看异步请求
}
异步执行任务
// RealCall.class
// 异步执行任务,所以需要将任务加入任务队列
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();
// 将任务加入执行队列,AsyncCall是真正执行异步任务的地方。
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
// Dispatcher.class
void enqueue(AsyncCall call) {
synchronized (this) {
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) {
// 这里根据host去异步执行队列和异步等待队列里面匹配call
AsyncCall existingCall = findExistingCallWithHost(call.host());
// 如果匹配到,则将当前的call复用之前的callsPerHost
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
//这里执行请求
promoteAndExecute();
}
// Dispatcher.class
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
// 遍历异步任务队列,并执行异步任务
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
// 当前执行的任务数不能超过 maxRequests 个数。
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
// 每个host 最大连接数
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
// 执行异步任务时,将当前关联的host个数自增1.
asyncCall.callsPerHost().incrementAndGet();
// 将当前的任务添加到执行任务集合中。
executableCalls.add(asyncCall);
// 将当前任务添加到正在执行的异步任务队列中。
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
// 上面相当于一次装载够需要执行的异步任务后,这里统一执行。
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
// 调用AsyncCall的方法,executorService()是一个线程池,所以AsyncCall最终会在这个线程池内执行任务。
asyncCall.executeOn(executorService());
}
return isRunning;
}
AsyncCall 类
/**
* 在介绍 AsyncCall 前,先来看一下 NamedRunnable 类,NamedRunnable 实现了 Runnable 接口。
* AsyncCall 继承 NamedRunnable
*/
// NamedRunnable.class
public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute(); //子类执行
} finally {
Thread.currentThread().setName(oldName);
}
}
// AsyncCall.class
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
//外面传入的任务最终在这里被线程池执行。
//执行这个方法之后,会执行NamedRunnable.run()方法,最终执行AsyncCall.execute()方法
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
// AsyncCall.class
protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();
try {
// 执行到这里,就跟同步方法里一样了,这里是最终执行网络请求的操作。
Response response = getResponseWithInterceptorChain();
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);
}
} catch (Throwable t) {
cancel();
if (!signalledCallback) {
IOException canceledException = new IOException("canceled due to " + t);
canceledException.addSuppressed(t);
responseCallback.onFailure(RealCall.this, canceledException);
}
throw t;
} finally {
// 执行完后,需要将当前执行的任务从runningAsyncCalls队列移除。
client.dispatcher().finished(this);
}
}
void finished(AsyncCall call) {
call.callsPerHost().decrementAndGet(); //将perHost关联的数量减1,一个perHost最多只能关联5个call
finished(runningAsyncCalls, call);
}
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
// 将指定的call从指定的队列中移除。
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
//在释放上个请求的同时,重新去readyAsyncCalls队列获取请求,直到队列中没有请求为止。
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
4. getResponseWithInterceptorChain()
getResponseWithInterceptorChain()
方法中添加了一系列的拦截器,这些拦截器实现了网络数据的获取。
- 这一系列拦截器怎么执行呢?
这里通过RealInterceptorChain
类将他们串行执行 (责任链模式)。
// RealCall.class
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
//这里添加了一系列的拦截器
List<Interceptor> interceptors = new ArrayList<>();
//添加用户应用层自定义的Interceptor
interceptors.addAll(client.interceptors());
//这个Interceptor是处理请求失败的重试,重定向
interceptors.add(new RetryAndFollowUpInterceptor(client));
//这个Interceptor工作是添加一些请求的头部或其他信息
//并对返回的Response做一些友好的处理(有一些信息你可能并不需要)
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//这个Interceptor的职责是判断缓存是否存在,读取缓存,更新缓存等等
interceptors.add(new CacheInterceptor(client.internalCache()));
//这个Interceptor的职责是建立客户端和服务器的连接
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//添加用户自定义的网络层拦截器
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
//一个包裹这request的chain
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
// 请求发出后获取到的响应数据
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
// RealInterceptorChain.class
public Response proceed(Request request) throws IOException {
return proceed(request, transmitter, exchange);
}
// RealInterceptorChain.class
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
// ...省略代码...
// interceptors表示之前的一串拦截器,index从0开始,proceed()方法每被执行一次,index都会增加1,所以相当于会获取到下一个拦截器(实现了拦截器的串行)。
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// ...省略代码...
return response;
}
// RetryAndFollowUpInterceptor.class
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
// ...省略代码...
// 这里会执行下一个
Response response = realChain.proceed(request, transmitter, null);
// ...省略代码...
}
四、小结
1. OkHttp 请求数据流程
- 通过
Builder 模式
构建一个 OkHttpClient 对象,用于请求数据。 - 通过
newRealCall()
方法构建一个 RealCall 对象,用于任务的调度(任务调度可以分为:同步、异步)。 - 使用
责任链模式
,实现了任务的分步处理。并对外提供了interceptors
拦截和networkInterceptors
拦截器功能,实现了对请求数据和响应数据的操作。
2. OkHttp 涉及的设计模式
序号 | 模式 | 应用场景 |
---|---|---|
1 | Builder 模式 | 构建 OkHttpClient |
2 | 工厂模式 | OkHttpClient 中构建 Call 对象 |
3 | 责任链模式 | RetryAndFollowUpInterceptor 拦截器 |
4 | 策略模式 | 缓存 |
5 | 生产者消费者模式 | 添加异步请求任务 |