对OkHttp介绍,我们分为使用篇和源码分析篇两个系列进行介绍。
在OkHttp使用篇当中,我们分几篇文章介绍了OkHttp框架的使用,但并没有去分析整个框架的源码和实现。这里单独开一个系列来捋一捋OkHttp3的源码,整个过程也是我自己学习的过程,所以有不好的地方还请见谅了。
下面就进入到本篇文章主题,从源码角度分析下整体的执行流程。在OkHttp3使用(二)-常用类介绍中,我们已经分析了整个执行流程,不过那个时候是从使用的角度,并没有深入去了解每个类的实现,这篇文章也可以算是对那篇文章的补充说明吧。
1 整体说明
这里照旧还是先放一张OkHttp执行的整体流程图
后续我们对整个执行流程的整体分析就是按照这个图所示进行的。所以先了解这个图,对后面我们流程跟踪是有帮助的。
除了了解流程图,这里我们先对整个OkHttp框架的设计模式做个简单的介绍:
- OkHttp整体上使用外观模式,通过OkHttpClient来统一对外暴露功能接口给使用者使用
- 在内部的模块上呢,又使用了建造者模式来进行对象的创建和管理,从而使得对于复杂对象的管理和扩展都有较好的支持
2 源码流程分析
2.1 同步请求源码分析
一般我们同步请求会按照如下方式进行使用:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
还是很简洁的,但是其实内部流程可就没有这么简洁了。先从我们new OkHttpClient进行说起。
2.1.1 OkHttpClient类介绍
这个类如果你去看源码,会发现其有将近1000行的代码,所以这里就不贴出源代码了。
这个类的设计是采用的外观模式。这种模式使得OkHttp的很多功能接口都封装在了内部,然后单独对外提供OkHttpClient这个类来暴露给外部使用者进行调用。所以之前我们介绍的时候也说了,我们可以通过这个类可以配置许许多多的东西:
final Dispatcher dispatcher;
final @Nullable Proxy proxy;
final List<Protocol> protocols;
final List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors;
final List<Interceptor> networkInterceptors;
final EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector;
final CookieJar cookieJar;
final @Nullable Cache cache;
final @Nullable InternalCache internalCache;
final SocketFactory socketFactory;
final SSLSocketFactory sslSocketFactory;
final CertificateChainCleaner certificateChainCleaner;
final HostnameVerifier hostnameVerifier;
final CertificatePinner certificatePinner;
final Authenticator proxyAuthenticator;
final Authenticator authenticator;
final ConnectionPool connectionPool;
final Dns dns;
final boolean followSslRedirects;
final boolean followRedirects;
final boolean retryOnConnectionFailure;
final int callTimeout;
final int connectTimeout;
final int readTimeout;
final int writeTimeout;
final int pingInterval;
上面列出了OkHttpClient类里面的大部分可配置属性,本篇文章对流程进行梳理,所以这里不对每个属性展开进行详细说明了。当然了,对于这些属性的配置都是通过内部类OkHttpClient.Builder
进行配置的。说完OkHttpClient,接着我们的流程,我们创建了Request对象。
2.1.2 Request和Response
这里我们将Request和Response放在一起进行说明,因为这两个类是对HTTP中的请求和响应的抽象。
2.1.2.1 Request
这个类比较简单,主要就是封装了请求的url、method、headers、requestBody几个属性
public final class Request {
final HttpUrl url;
final String method;
final Headers headers; // 对HTTP中请求头信息的封装
final @Nullable RequestBody body; // 对HTTP请求体信息的封装
final Map<Class<?>, Object> tags;
private volatile @Nullable CacheControl cacheControl; // Lazily initialized.
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);
}
public HttpUrl url() {
return url;
}
public String method() {
return method;
}
public Headers headers() {
return headers;
}
public @Nullable String header(String name) {
return headers.get(name);
}
public List<String> headers(String name) {
return headers.values(name);
}
public @Nullable RequestBody body() {
return body;
}
// 获取当前请求中tag,我们可以通过其内部类Builder.tag()方法设置一个tag,以便我们在自定义Interceptor等情形下可以通过该tag做一些操作
public @Nullable Object tag() {
return tag(Object.class);
}
// 同上,只是该方法返回一个指定类型的tag
public @Nullable <T> T tag(Class<? extends T> type) {
return type.cast(tags.get(type));
}
public Builder newBuilder() {
return new Builder(this);
}
// 获得本次请求的缓存控制策略(OkHttp中对缓存策略的控制信息抽象为了CacheControl类)
public CacheControl cacheControl() {
CacheControl result = cacheControl;
return result != null ? result : (cacheControl = CacheControl.parse(headers));
}
// 判断当前请求是否是https协议
public boolean isHttps() {
return url.isHttps();
}
}
由于这里是采用的Builder模式,所以属性的设置都统一放到内部类Request.Builder
中去了:
public static class Builder {
@Nullable HttpUrl url;
String method;
// 这里涉及到的Headers也是用Builder模式进行构建
Headers.Builder headers;
@Nullable RequestBody body;
// 用于存放我们设置的tag
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();
}
public Builder url(HttpUrl url) {
if (url == null) throw new NullPointerException("url == null");
this.url = url;
return this;
}
// 设置url
public Builder url(String url) {
if (url == null) throw new NullPointerException("url == null");
// Silently replace web socket URLs with HTTP URLs.
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}
return url(HttpUrl.get(url));
}
// 重置设置url的方法
public Builder url(URL url) {
if (url == null) throw new NullPointerException("url == null");
return url(HttpUrl.get(url.toString()));
}
// 设置header,如果之前有同name的值,会被替换
public Builder header(String name, String value) {
headers.set(name, value);
return this;
}
// 添加header,如果之前有同name的值,不会覆盖,会形成一个name对应多个value的情况
public Builder addHeader(String name, String value) {
headers.add(name, value);
return this;
}
// 移除指定name的header
public Builder removeHeader(String name) {
headers.removeAll(name);
return this;
}
// 移除指定header
public Builder headers(Headers headers) {
this.headers = headers.newBuilder();
return this;
}
// 设置缓存策略
public Builder cacheControl(CacheControl cacheControl) {
String value = cacheControl.toString();
if (value.isEmpty()) return removeHeader("Cache-Control");
return header("Cache-Control", value);
}
// 下面就是实现不同的请求方式 就不再做一一说明了
public Builder get() {
return method("GET", null);
}
public Builder head() {
return method("HEAD", null);
}
public Builder post(RequestBody body) {
return method("POST", body);
}
public Builder delete(@Nullable RequestBody body) {
return method("DELETE", body);
}
public Builder delete() {
return delete(Util.EMPTY_REQUEST);
}
public Builder put(RequestBody body) {
return method("PUT", body);
}
public Builder patch(RequestBody body) {
return method("PATCH", body);
}
// 设置请求方式和请求体
public Builder method(String method, @Nullable RequestBody body) {
if (method == null) throw new NullPointerException("method == null");
if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
if (body != null && !HttpMethod.permitsRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must not have a request body.");
}
if (body == null && HttpMethod.requiresRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must have a request body.");
}
this.method = method;
this.body = body;
return this;
}
// 设置tag
public Builder tag(@Nullable Object tag) {
return tag(Object.class, tag);
}
// 设置指定类型的tag
public <T> Builder tag(Class<? super T> type, @Nullable T tag) {
if (type == null) throw new NullPointerException("type == null");
if (tag == null) {
tags.remove(type);
} else {
if (tags.isEmpty()) tags = new LinkedHashMap<>();
tags.put(type, type.cast(tag));
}
return this;
}
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
}
这个类不复杂,就是对HTTP中的请求进行了抽象,将对应的信息进行了封装。url和method等简单的信息,直接封装为对应属性即可,复杂的请求头和请求体信息用Headers和RequestBody两个类来进一步进行处理。
2.1.2.2 Headers
通过前面Request的源码,我们知道请求体都是封装在这个类里面的。
由于整个Headers的源码有400多行,这里就不全部贴出了,我们看一些常用的关键的信息即可:
public final class Headers {
// 用数组存储请求头的信息
private final String[] namesAndValues;
Headers(Builder builder) {
this.namesAndValues = builder.namesAndValues.toArray(new String[builder.namesAndValues.size()]);
}
private Headers(String[] namesAndValues) {
this.namesAndValues = namesAndValues;
}
// 获取指定key最后(因为一个key可能对应多个value,所以这里是强调是最后一个)添加的value
public @Nullable String get(String name) {
return get(namesAndValues, name);
}
// 同上,只是这里获取的是Date(如果你添加了日期参数)
public @Nullable Date getDate(String name) {
String value = get(name);
return value != null ? HttpDate.parse(value) : null;
}
// 请求体打下(总共有多少对键值对)
public int size() {
return namesAndValues.length / 2;
}
// 通过索引获取对应的key值
public String name(int index) {
return namesAndValues[index * 2];
}
// 通过索引获取对应的value值
public String value(int index) {
return namesAndValues[index * 2 + 1];
}
// 下面还有些类似的方法 这里就不过多介绍了
……
public Builder newBuilder() {
Builder result = new Builder();
Collections.addAll(result.namesAndValues, namesAndValues);
return result;
}
// 将请求体从数组转为Map
public Map<String, List<String>> toMultimap() {
Map<String, List<String>> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
for (int i = 0, size = size(); i < size; i++) {
String name = name(i).toLowerCase(Locale.US);
List<String> values = result.get(name);
if (values == null) {
values = new ArrayList<>(2);
result.put(name, values);
}
values.add(value(i));
}
return result;
}
// 上面很多获取name或者value的方法都是通过该方法获取
private static String get(String[] namesAndValues, String name) {
// 这里需要注意是从后往前取,有对应值的时候就返回,所以前面很多是取最后一个值原因就在这
for (int i = namesAndValues.length - 2; i >= 0; i -= 2) { // 同时这里以2为一个步长
if (name.equalsIgnoreCase(namesAndValues[i])) {
return namesAndValues[i + 1];
}
}
return null;
}
……
public static final class Builder {
// Builder里面是用ArrayList来进行维护增删改查的操作
final List<String> namesAndValues = new ArrayList<>(20);
……
// 常用的添加请求体的操作
public Builder add(String name, String value) {
checkName(name);
checkValue(value, name);
// 通过addLenient
return addLenient(name, value);
}
// 真正添加的地方
Builder addLenient(String name, String value) {
namesAndValues.add(name);
namesAndValues.add(value.trim());
return this;
}
……
// 通过set方法设置请求体
public Builder set(String name, String value) {
checkName(name);
checkValue(value, name);
// 这里会先移除之前已经添加了该name的所有请求体
removeAll(name);
//通过addLenient添加
addLenient(name, value);
return this;
}
// 构建Headers对象
public Headers build() {
return new Headers(this);
}
}
}
这个类源代码并没有全部贴出,但是找了一些具有代表性的方法进行说么,其他的功能和上面的方法类似,只是手段稍微有点不同。比如添加请求头,我们这里只贴出了添加指定name和value的方法,其他的还有直接通过"namve:value"这种格式字符串添加,或者直接添加日期参数。但是原理都一样,所以就不全部一个一个解释了。
上面的方法注释也都比较清楚了,这里就不再啰嗦解释了。只是注意两点就是
add
和set
两个方法不同的地方在于set
多了一个’remove’操作。也就验证了之前所述两者的不同。- 另外这里并没有使用Map来维护请求头的信息,而是用
ArrayList
来维护,最终build()
构建Headers的时候,再转为数组。这样设计目的是为了应对请求头中同一name可能对应多个value的情况。在维护信息的时候,采取没2个String
的值为一个header步长。从上面的get(String[] namesAndValues, String name)
方法取值的时候就能看出来了。
2.1.2.3 RequestBody
说完请求头,接下来说说请求体的信息封装类RequestBody
public abstract class RequestBody {}
从声明中看到这是一个抽象类,使用的时候,需要我们实现它的两个抽象方法:
// 返回当前请求的MIME类型
public abstract @Nullable MediaType contentType();
// 将请求信息写入到BufferedSink
public abstract void writeTo(BufferedSink sink) throws IOException;
具体使用在OkHttp3使用(一)-基本使用中提交流的小节当中有介绍。
当然考虑周到的OkHttp的开发者,是不会让我们每次使用都自己去重新继承这个类来实现请求体封装的。如果是那样,估计就没这么多人会使用这个框架了。所以OkHttp提供了常用的两个实现类给我们日常开发使用。分别是:FormBody
(表单提交)和MultipartBody
(分块提交,一般上传文件使用)分别对应两种不同的MIME类型
FormBody :“application/x-www-form-urlencoded”
MultipartBody:“multipart/”+xxx.
FormBody
和MultipartBody
的实现都不复杂,而且我们在OkHttp3使用(二)-常用类介绍也对这两个类做了介绍,这里就不再赘述了。
说完Request,再说下对应HTTP协议中响应的Response
类
2.1.2.4 Response
有了上面对Request的介绍,这个类理解起来也就比较简单了:
public final class Response implements Closeable {
final Request request;
final Protocol protocol;
final int code;
final String message;
final @Nullable Handshake handshake;
final Headers headers;
// 相应体
final @Nullable ResponseBody body;
// 网络请求的响应
final @Nullable Response networkResponse;
// 缓存响应
final @Nullable Response cacheResponse;
// 如果重定向等操作 代表重定向之前的响应
final @Nullable Response priorResponse;
// 发送请求的时间
final long sentRequestAtMillis;
// 收到响应的时间
final long receivedResponseAtMillis;
// 缓存控制
private volatile @Nullable CacheControl cacheControl; // Lazily initialized.
Response(Builder builder) {
this.request = builder.request;
this.protocol = builder.protocol;
this.code = builder.code;
this.message = builder.message;
this.handshake = builder.handshake;
this.headers = builder.headers.build();
this.body = builder.body;
this.networkResponse = builder.networkResponse;
this.cacheResponse = builder.cacheResponse;
this.priorResponse = builder.priorResponse;
this.sentRequestAtMillis = builder.sentRequestAtMillis;
this.receivedResponseAtMillis = builder.receivedResponseAtMillis;
}
// 获取该次请求的Request
public Request request() {
return request;
}
// 返回采用的协议(如HTTP1/HTTP2等)
public Protocol protocol() {
return protocol;
}
// 返回码
public int code() {
return code;
}
// 请求是否成功
public boolean isSuccessful() {
return code >= 200 && code < 300;
}
// 响应当中的提示信息
public String message() {
return message;
}
/**
* TLS相关
*/
public @Nullable Handshake handshake() {
return handshake;
}
// 响应头信息
public List<String> headers(String name) {
return headers.values(name);
}
// 响应头信息
public @Nullable String header(String name) {
return header(name, null);
}
// 响应头信息
public @Nullable String header(String name, @Nullable String defaultValue) {
String result = headers.get(name);
return result != null ? result : defaultValue;
}
// 响应头信息
public Headers headers() {
return headers;
}
// 获取指定数量的响应信息
public ResponseBody peekBody(long byteCount) throws IOException {
BufferedSource peeked = body.source().peek();
Buffer buffer = new Buffer();
peeked.request(byteCount);
buffer.write(peeked, Math.min(byteCount, peeked.getBuffer().size()));
return ResponseBody.create(body.contentType(), buffer.size(), buffer);
}
// 获取相应体
public @Nullable ResponseBody body() {
return body;
}
}
代码里面已经注释的比较清楚了,加上之前对Request的解析,Response也是同样的实现逻辑,将响应头和响应体抽象为了Headers
和ResponseBody
,这两个类也很简单,篇幅有限,所以这里就不再啰嗦了,大家翻出源码看看就应该清楚了。
接着最开始那个示例的流程,创建好Request之后,就交给OkHttpClient的newCall(request)
方法了,并返回了Call
对象。那我们看下这个方法里面做了什么
2.1.3 Call的创建
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
简单,就一行代码,调用了RealCall.newRealCall()
方法,并传入了OkHttpClient
对象和Request
对象。继续跟下去看看:
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;
}
这里也就比较简单,就是通过传入的OkHttpClient
和Request
创建了RealCall
对象并返回了该对象。这样我们就创建好了Call对象。
2.1.4 执行Call的excute()方法发起请求
上面创建好Call对象后,接着下面的流程就是调用了Call的excute()
方法。这里我们知道,Call的实际类型是RealCall。所以进入RealCall的excute()
方法:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
// 通过Dispatcher进行请求任务管理,后面的章节会细讲,这里不影响我们队流程的分析,跳过
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);
}
}
上面我们通过第一个if判断可以看出,一个Call对象只能请求一次,否则就会报错。
最终是通过调用getResponseWithInterceptorChain();
获得响应的信息并返回给调用者。
getResponseWithInterceptorChain();
里面就是通过发起网络请求获取数据的过程,里面涉及到非常多的东西,后续我们的章节会进入到该方法进行详细说明,这里我们暂时知道该方法会发起网络请求,并且返回数据给我们就行了。
这样我们整个同步请求的流程就算是走完了,下面接着说说异步请求的流程。
2.2 异步请求执行流程
使用过异步请求的朋友应该知道异步请求很多步骤和上面同步请求是一样的,所以后续我们分析的时候,同样的步骤我们就直接跳过了。
我们还是先看一段异步请求的代码:
OkHttpClient client = new OkHttpClient();
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) {
……
}
@Override public void onResponse(Call call, Response response) throws IOException {
……
}
}
});
可以看到前面一直到Call对象的创建。流程和同步请求都是一致的,这里就不做分析了。最后通过调用Call.enqueue(CallBack)
方法就完成了异步请求,我们进去看下这个方法里面做了什么:
@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));
执行的。之前我们分析同步流程的时候,也有类似的流程,只是最终调用的是client.dispatcher().executed(this);
。前面也说了,这里涉及到OkHttp对请求任务的调度管理,后面章节会详细介绍。这里我们先知道通过该处调用后。最终会交给AsyncCall的executeOn(ExecutorService executorService)
方法进行处理。就进入AsyncCall这个类中看看。
2.2.1 AsyncCall中流程
接着上面说的流程,会进入executeOn(ExecutorService executorService)
方法
// 这里通过线程池执行任务。
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
// 通过线程池执行
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
eventListener.callFailed(RealCall.this, ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
这个方法里面也比较简单,调用了executorService.execute(this);
方法。executorService是一个线程池对象,了解线程池知识的朋友应该知道,最终会进入到该类的run
方法(这里补充AsyncCall继承自NamedRunnable,其又实现了Runnable接口)。但是你会发现AsyncCall
中没有run
方法,它的实现是在其父类NamedRunnable
中:
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 {
// run方法里面最终调用了excute()方法。
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
// 抽象方法,由其子类实现(该处为AsyncCall)
protected abstract void execute();
}
该类的run方法里面,又调用了execute();
方法,这是一个抽象方法,所以实现在其子类(AsyncCall)中,又回到AysncCall
:
final class AsyncCall extends NamedRunnable {
// 这个就是我们的异步回调的CallBack对象,通过上述AsyncCAll构造方法传入。
private final Callback responseCallback;
……
// 真正发起请求的地方
@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()
方法获取到Response
。获取到之后,通过我们之前通过AsyncCall
构造方法传入的Callback
对象,回调对应的成功、失败的方法即可。
通过前面的分析知道,是通过线程池来执行AsyncCall
的,所以该处运行的线程是在Runnable的run
方法所在的线程,回调并没有进行线程切换,也是发生在该线程,并不是在主线程,所以我们使用的时候,要注意不要在回调方法中做更新UI的操作。
到此,我们同步请求和异步请求的流程就分析完了,这个时候再回过去看最开始那张流程图,是不是感觉就清晰了许多。