317 lines
12 KiB
Markdown
317 lines
12 KiB
Markdown
---
|
||
title: OkHTTP3 同步基本流程
|
||
date: 2020-06-08 16:15:55
|
||
tags: [kotlin]
|
||
categories: [kotlin]
|
||
author: Karl
|
||
---
|
||
# Okhttp3 同步基本流程
|
||
## 流程分析
|
||
### OkhttpClient
|
||
使用Okhttp,首先需要实例化一个OkhttpClient对象。支持2种构造方式
|
||
1. 默认方式
|
||
~~~
|
||
val client = OkhttpClient()
|
||
~~~
|
||
2.使用建造者方式
|
||
~~~
|
||
val client = OkhttpClient.Builder()
|
||
.build()
|
||
~~~
|
||
他们的具体实现为
|
||
~~~
|
||
public OkhttpClient() {
|
||
this(new Builder());
|
||
}
|
||
~~~
|
||
|
||
可以看到这种方式,不需要配置任何参数,也就是说基本参数都是默认的,调用的是下面的构造函数。
|
||
~~~
|
||
OkHttpClient(Builder builder) {
|
||
。。。。
|
||
}
|
||
~~~
|
||
建造者返回一个OkhttpClient实例
|
||
~~~
|
||
public OkHttpClient build() {
|
||
return new OkHttpClient(this);
|
||
}
|
||
~~~
|
||
|
||
我们查看源码发现默认方式其实是使用了2种设计模式:
|
||
1.外观模式
|
||
2.建造者模式
|
||
|
||
|
||
### Request
|
||
OkhttpClient完成后就需要构建一个Request对象,我们查看Requests对象找不到过多的构造函数,唯一的构造函数是这样的
|
||
~~~
|
||
Request(Builder builder) {
|
||
this.url = builder.url;
|
||
this.method = builder.method;
|
||
this.headers = builder.headers.build();
|
||
this.body = builder.body;
|
||
this.tags = Util.immutableMap(builder.tags);
|
||
}
|
||
~~~
|
||
这就是在告诉我们,创建Requests实例需要使用Builder模式进行构建
|
||
~~~
|
||
|
||
public Builder newBuilder() {
|
||
return new Builder(this);
|
||
}
|
||
|
||
|
||
public static class Builder {
|
||
@Nullable HttpUrl url;
|
||
String method;
|
||
Headers.Builder headers;
|
||
@Nullable RequestBody body;
|
||
|
||
Map<Class<?>, Object> tags = Collections.emptyMap();
|
||
|
||
public Builder() {
|
||
this.method = "GET";
|
||
this.headers = new Headers.Builder();
|
||
}
|
||
Builder(Request request) {
|
||
this.url = request.url;
|
||
this.method = request.method;
|
||
this.body = request.body;
|
||
this.tags = request.tags.isEmpty() ? Collections.<Class<?>, Object>emptyMap() : new LinkedHashMap<>(request.tags);
|
||
this.headers = request.headers.newBuilder();
|
||
。。。
|
||
}
|
||
~~~
|
||
查看其他的对象发现也是基本使用Builder模式进行构建,所以此处就不在继续查看
|
||
|
||
### 同步请求
|
||
我们需要构建一个call,一般使用 val call = okhttpClient.newCall(request)构建,去查看OkHttpClient源码
|
||
~~~
|
||
@Override public Call newCall(Request request) { return RealCall.newRealCall(this, request,
|
||
false /* for web socket */);
|
||
}
|
||
~~~
|
||
@Override 看到这个注解我们就要意识到,这个方法不是继承就是实现接口
|
||
我们继续往源码翻,发现他是实现了Call.Factory接口
|
||
~~~
|
||
interface Factory {
|
||
Call newCall(Request request);
|
||
}
|
||
~~~
|
||
这里又使用了新的设计模式,工厂设计模式,具体的实例由其子类产生。
|
||
将构建的细节交给具体实现,顶层只需要拿到Call对象即可。
|
||
我们继续查看RealCall中的newRealCall方法
|
||
~~~
|
||
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.eventListener = client.eventListenerFactory().create(call);
|
||
return call;
|
||
}
|
||
~~~
|
||
这里是一个静态方法, 创建了一个EventListener对象,名字可以看出,这是一个监听事件的对象从构造方法看出,这里使用了工厂设计创建的eventListener
|
||
继续查看RealCall的构造函数
|
||
~~~
|
||
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
|
||
this.client = client;
|
||
this.originalRequest = originalRequest;
|
||
this.forWebSocket = forWebSocket;
|
||
//添加了RetryAndFollowUpInterceptor拦截器
|
||
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
|
||
this.timeout = new AsyncTimeout() {
|
||
@Override protected void timedOut() {
|
||
cancel();
|
||
}
|
||
};
|
||
this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
|
||
}
|
||
~~~
|
||
核心来了,执行请求
|
||
我们首先看的是同步请求
|
||
~~~
|
||
Response execute() throws IOException;
|
||
|
||
/*** Schedules the request to be executed at some
|
||
point in the future. * * <p>The {@link OkHttpClient#dispatcher
|
||
dispatcher} defines when the request will run:
|
||
usually * immediately unless there are several other
|
||
requests currently being executed. * * <p>This client will later call back {@code responseCallback} with either an HTTP response
|
||
or a * failure exception. * * @throws IllegalStateException when the call
|
||
has already been executed. */
|
||
//异步请求方法
|
||
void enqueue(Callback responseCallback);
|
||
~~~
|
||
我们查看具体的实现,在RealCall中实现了这个方法
|
||
~~~
|
||
@Override public Response execute() throws IOException {
|
||
synchronized (this) {
|
||
if (executed) throw new IllegalStateException("Already Executed"); executed = true;
|
||
}
|
||
captureCallStackTrace();
|
||
timeout.enter();
|
||
eventListener.callStart(this);
|
||
try {
|
||
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 {
|
||
client.dispatcher().finished(this);
|
||
}
|
||
}
|
||
~~~
|
||
synchronized加入了对象锁,防止多线程同时调用,这里先判断一下executed是否为true判断当前call是否被执行了,如果为ture,则抛出异常,没有则设置为true
|
||
captureCallStackTrace()
|
||
~~~
|
||
private void captureCallStackTrace() {
|
||
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
|
||
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
|
||
}
|
||
~~~
|
||
源码来看是为了前文添加的过滤器添加一系列的堆栈信息
|
||
timeout.enter();
|
||
这里应该是进行超时之类的校验,不深究继续往下看
|
||
eventListener.callStart(this);
|
||
可以看到前面构建的eventListener起到作用了,这里先回调callStart方法。
|
||
client.dispatcher().executed(this);
|
||
这里出现一个新的东东: dispatcher,所以我们回到OkhttpClient代码中去查看这个到底是什么东西
|
||
~~~
|
||
public Dispatcher dispatcher() { return dispatcher;}
|
||
~~~
|
||
看到是返回了一个Dispathcer对象,没有其他任何东西,我们继续点进去看看Dispatcher是什么
|
||
先对Dispatcher的成员变量有个认识
|
||
~~~
|
||
/**
|
||
* Policy on when async requests are executed.
|
||
*
|
||
* <p>Each dispatcher uses an {@link ExecutorService} to run calls internally. If you supply your
|
||
* own executor, it should be able to run {@linkplain #getMaxRequests the configured maximum} number
|
||
* of calls concurrently. */
|
||
|
||
public final class Dispatcher {
|
||
private int maxRequests = 64;
|
||
private int maxRequestsPerHost = 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<>();
|
||
}
|
||
~~~
|
||
从注释中我们可以知道这是一个关于何时执行异步请求的策略
|
||
可以看到,最大请求数64,同一个host请求5,这里用三个队列ArrayDeque用于保存Call对象,分为三种状态异步等待,同步running,异步running。
|
||
前方使用的client.dispatcher().executed(this);
|
||
executed在Dispatcher中为
|
||
~~~
|
||
synchronized void executed(RealCall call) { runningSyncCalls.add(call);}
|
||
~~~
|
||
将当前call塞入同步running队列中
|
||
Response result = getResponseWithInterceptorChain();
|
||
getResponseWithInterceptorChain() 这个方法就是OkHtpp中的核心
|
||
我们点开这个方法
|
||
~~~
|
||
Response getResponseWithInterceptorChain() throws IOException {
|
||
// Build a full stack of interceptors.
|
||
List<Interceptor> interceptors = new ArrayList<>();
|
||
//自定义的拦截器
|
||
interceptors.addAll(client.interceptors());
|
||
//失败和重定向过滤器
|
||
interceptors.add(retryAndFollowUpInterceptor);
|
||
//封装request和response过滤器
|
||
interceptors.add(new BridgeInterceptor(client.cookieJar()));
|
||
//缓存相关的过滤器,负责读取缓存直接返回、更新缓存
|
||
interceptors.add(new CacheInterceptor(client.internalCache()));
|
||
//负责和服务器建立连接,连接池等
|
||
interceptors.add(new ConnectInterceptor(client));
|
||
if (!forWebSocket) {
|
||
//配置 OkHttpClient 时设置的 networkInterceptors
|
||
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);
|
||
}
|
||
~~~
|
||
注意Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
|
||
originalRequest, this, eventListener, client.connectTimeoutMillis(),
|
||
client.readTimeoutMillis(), client.writeTimeoutMillis());中的0
|
||
我们看到这个方法是添加拦截器,然后调用chian中的proceed方法,我们继续查看proceed方法
|
||
~~~
|
||
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
|
||
RealConnection connection) throws IOException {
|
||
if (index >= interceptors.size()) throw new AssertionError();
|
||
|
||
calls++;
|
||
|
||
// If we already have a stream, confirm that the incoming request will use it.
|
||
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
|
||
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
|
||
+ " must retain the same host and port");
|
||
}
|
||
|
||
// If we already have a stream, confirm that this is the only call to chain.proceed().
|
||
if (this.httpCodec != null && calls > 1) {
|
||
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
|
||
+ " must call proceed() exactly once");
|
||
}
|
||
|
||
// Call the next interceptor in the chain.
|
||
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
|
||
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
|
||
writeTimeout);
|
||
Interceptor interceptor = interceptors.get(index);
|
||
Response response = interceptor.intercept(next);
|
||
|
||
// Confirm that the next interceptor made its required call to chain.proceed().
|
||
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
|
||
throw new IllegalStateException("network interceptor " + interceptor
|
||
+ " must call proceed() exactly once");
|
||
}
|
||
|
||
// Confirm that the intercepted response isn't null.
|
||
if (response == null) {
|
||
throw new NullPointerException("interceptor " + interceptor + " returned null");
|
||
}
|
||
|
||
if (response.body() == null) {
|
||
throw new IllegalStateException(
|
||
"interceptor " + interceptor + " returned a response with no body");
|
||
}
|
||
|
||
return response;
|
||
}
|
||
~~~
|
||
初看很乱但核心在这几行代码
|
||
~~~
|
||
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
|
||
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
|
||
writeTimeout);
|
||
Interceptor interceptor = interceptors.get(index);
|
||
Response response = interceptor.intercept(next);
|
||
~~~
|
||
这样就很清晰了,这里index就是我们刚才的0,也就是从0开始,如果index超过了过滤器的个数抛出异常,后面会再new一个RealInterceptorChain,而且会将参数传递,并且index+1了,接着获取index的interceptor,并调用intercept方法,传入新new的next对象,这里用了递归的思想来完成遍历。这里也就是okhttp最经典的责任链模式。在这里找不到退出的地方,我们往前getResponseWithInterceptorChain()方法interceptors.add(new CallServerInterceptor(forWebSocket));这是最后加载的一个拦截器,我们进去查看
|
||
~~~
|
||
@Override public Response intercept(Chain chain) throws IOException {
|
||
...
|
||
return response;
|
||
}
|
||
~~~
|
||
篇幅太长,我们省略一下 看见这个拦截器并没有继续调用链中intercept方法,而是直接返回的response对象
|
||
同步的我们就分析到这里
|
||
|
||
Author: Karl
|