Okhttp3源码分析
okhttp3源码分析基于okhttp3.10.0。
关于okhttp的特点及相关功能的介绍可以查看官网的介绍:
基本使用
使用okhttp发起一次网络请求,只需要以下流程即可:
- 创建OkHttpClient
- 创建Request对象
- 创建一个Call对象,用于发起请求
- 发起网络请求(同步请求execute; 异步请求enqueue)
代码演示:
public void request() {
// 1、创建OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient();
// 2、创建Request请求
Request request = new Request.Builder()
.url("https://blog.csdn.net/kaifa1321")
.build();
// 3、创建一个Call对象,用于发起请求
Call call = okHttpClient.newCall(request);
// 4、发起网络请求
// 4.1、执行一个同步请求,获取Response
Response response = call.execute();
// 4.2、执行一个异步请求,通过Callback获取Response
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 处理响应
String string = response.body().string();
}
});
}
源码分析
针对上文中提到的okhttp的使用流程,这里将对这些流程的源码逐一分析。
创建OkHttpClient实例
OkHttpClient对象的创建,okhttp中提供了两种方式:
- 通过无参数构造方法创建OkHttpClient
- 通过OkHttpClient内部类Builder,使用建造者模式创建OkHttpClient实例
// 1、构造方法创建OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient();
// 2、通过Builder创建OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(null)
.cache(null)
.build();
OkHttpClient实例化过程
这里看下OkHttpClient的构造方法:
public OkHttpClient() {
this(new OkHttpClient());
}
OkHttpClient(Builder builder) {
// 这不是该方法的完成代码,具体请看源码
this.dispatcher = builder.dispatcher;
this.interceptors = Util.immutableList(builder.interceptors);
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
}
通过上面源码可以得知,OkHttpClient的无参构造方法最后也是通过创建一个Builder对象,完成OkHttpClient的实例化。其实无参参构造方法实例化OkHttpClient和通过Builder.build()方法实例化原理上完全一样,都是通过创建一个Builder对象初始化OkHttpClient,唯一不同的是Builder对象参数(系统默认和开发者自己设置)
这里看下Builder.build()方法
public OkHttpClient build() {
// 这个this就是Builder对象本身
return new OkHttpClient(this);
}
OkHttpClient通过建造者模式完成实例化,这种创建模式在okhttp中有使用非常多,例如Request、Response的创建都使用了该模式。
定制化OkHttpClient实例
在okhttp的官网中建议开发者在使用okhttp时,OkHttpClient的实例最好是通过单一实例,这样可以更好的管理okhttp的连接池和线程池。
但是,一般在应用开发中,会遇到一些特殊的请求,比如添加一个拦截器、添加缓存等的操作。如果我们直接修改单一实例的OkHttpClient,这样势必会影响其他的请求,okhttp也考虑到了这种情况,也给我们提供给了定制化OkHttpClient实例的方法newBuidler。
OkHttpClient client = new OkHttpClient();
client.newBuilder()
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build();
这里看下newBuilder方法
public Builder newBuilder() {
// this就是OkHttpClient单一实例
return new Builder(this);
}
通过已有的OkHttpClient实例创建一个新的Builder对象,这个Builder对象默认持有原来OkHttpClient实例的属性,这样就可以完全复用存在的OkHttpClient的相关设置,只需要对一些特定的属性做出修改即可。例如readTimeout,writeTimeout等。
创建Request对象
Request对象的主要作用就是封装Http请求的主要参数,这些参数包括:
- url
- http请求方式:GET、POST等
- 请求体RequestBody,POST等请求方式时需要
- http请求头
Request对象和OkHttpClient一样,也是通过建造者模式创建的.
创建Request对象:
Request request = new Request.Builder()
.url("https://api.github.com/repos/square/okhttp/issues")
.header("User-Agent", "OkHttp Headers.java")
.addHeader("Accept", "application/json; q=0.5")
.addHeader("Accept", "application/vnd.github.v3+json")
.post(RequestBody.create(MediaType, File))
.build();
Request的创建流程相对简单,在上面中基本能够一目了然,这里就不做过多的介绍了。
在上面的代码示例中,需要注意的就是在Request中添加Header请求头的方法header()和addHeader()
在Request的源码中也对两个方法做了具体说明:
- header():设置唯一的请求头键值对,如果存在相同name的请求头,value将会被覆盖。
- addHeader():对相同name的请求头可以设置多个Value
RequestBody
在Request的创建过程中,如果该Request是一个POST请求,那么需要为该Request创建一个请求体RequestBody,RequestBody的创建需要调用静态方法create(),RequestBody中有几个重载的create()方法,主要针对不同的请求体类型创建请求体实例。
这里看下RequestBody类的具体内容。
public abstract class RequestBody {
// 请求体类型
public abstract @Nullable MediaType contentType();
// 请求体长度
public long contentLength() throws IOException {
return -1;
}
// 将请求体内容写到sink,sink是okio中的类,可以理解成输出流
public abstract void writeTo(BufferedSink sink) throws IOException;
/**
* 创建一个请求体,
*/
public static RequestBody create(@Nullable MediaType contentType, String content) {
// 使用UTF-8编码
Charset charset = Util.UTF_8;
if (contentType != null) {
charset = contentType.charset();
if (charset == null) {
charset = Util.UTF_8;
contentType = MediaType.parse(contentType + "; charset=utf-8");
}
}
byte[] bytes = content.getBytes(charset);
return create(contentType, bytes);
}
/**
* 创建请求体
*/
public static RequestBody create(
final @Nullable MediaType contentType, final ByteString content) {
return new RequestBody() {
@Override public @Nullable MediaType contentType() {
return contentType;
}
@Override public long contentLength() throws IOException {
return content.size();
}
@Override public void writeTo(BufferedSink sink) throws IOException {
sink.write(content);
}
};
}
/**
* 创建请求体
*/
public static RequestBody create(final @Nullable MediaType contentType, final byte[] content) {
return create(contentType, content, 0, content.length);
}
/**
* 创建请求体
*/
public static RequestBody create(final @Nullable MediaType contentType, final byte[] content,
final int offset, final int byteCount) {
if (content == null) throw new NullPointerException("content == null");
Util.checkOffsetAndCount(content.length, offset, byteCount);
return new RequestBody() {
@Override public @Nullable MediaType contentType() {
return contentType;
}
@Override public long contentLength() {
return byteCount;
}
@Override public void writeTo(BufferedSink sink) throws IOException {
// 将请求体内容写出到sink
sink.write(content, offset, byteCount);
}
};
}
/**
* 创建一个请求体,请求体类型为file,一般文件上传时使用
*/
public static RequestBody create(final @Nullable MediaType contentType, final File file) {
if (file == null) throw new NullPointerException("content == null");
return new RequestBody() {
@Override public @Nullable MediaType contentType() {
return contentType;
}
@Override public long contentLength() {
return file.length();
}
@Override public void writeTo(BufferedSink sink) throws IOException {
Source source = null;
try {
source = Okio.source(file);
sink.writeAll(source);
} finally {
Util.closeQuietly(source);
}
}
};
}
}
这里给出核心代码
public static class Builder {
Headers.Builder headers;
public Builder header(String name, String value) {
headers.set(name, value);
return this;
}
public Builder addHeader(String name, String value) {
headers.add(name, value);
return this;
}
}
public final class Headers {
final List<String> namesAndValues = new ArrayList<>(20);
// 添加一组键值对
public Builder add(String name, String value) {
checkNameAndValue(name, value);
return addLenient(name, value);
}
// 添加唯一键值对
public Builder set(String name, String value) {
checkNameAndValue(name, value);
// 如果存在,移除
removeAll(name);
addLenient(name, value);
return this;
}
// 添加到list集合中
Builder addLenient(String name, String value) {
namesAndValues.add(name);
namesAndValues.add(value.trim());
return this;
}
}
创建Call对象
// 3、创建一个Call启动请求
Call call = okHttpClient.newCall(request);
在okHttpClient.newCall()方法方法中通过调用RealCall.newRealCall()方法获取一个Call(RealCall)实例。
@Override
public Call newCall(Request request) {
// 获取一个Call(RealCall)对象
return RealCall.newRealCall(this, request, false /* for web socket */);
}
发起网络请求
详见下节:Call接口及实现类RealCall
Call接口及实现类RealCall
Call
Call接口,主要实现网络请求调用。
- 通过execute方法实现同步请求获取请求响应。
- 通过enqueue方法实现异步请求通过CallBack回调获取请求响应。
Call接口源码
public interface Call extends Cloneable {
// 返回原始请求对象
Request request();
// 同步请求
Response execute() throws IOException;
// 异步请求
void enqueue(Callback responseCallback);
// 取消请求
void cancel();
// 是否正在执行
boolean isExecuted();
// 是否取消
boolean isCanceled();
Call clone();
interface Factory {
Call newCall(Request request);
}
}
RealCall
RealCall是Call接口的唯一实现类,因此okhttp的网络请求都是在RealCall类里面实现的。
所以这里将会对RealCall做详细的分析。
创建RealCall实例
在上文中说到,RealCall对象是在okHttpClient.newCall()方法中通过调用RealCall.newRealCall方法完成创建的。
这里看下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;
}
在该方法中,主要就是通过构造方法完成RealCall对象的创建,并且该RealCall对象持有了OkHttpClient和Requst实例。
RealCall对象创建以后,就可以开始正式的网络请求了,这里主要分为同步请求和异步请求。
// 执行一个同步请求,获取Response
try {
Response response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
// 执行一个异步请求,通过Callback获取Response
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 处理响应
String string = response.body().string();
}
});
这里对这两个请求方式一一分析。
execute同步请求
RealCall.execute()
public Response execute() throws IOException {
// 1、首先判断该Call是否执行
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
// 2、调用OkHttpClient中的dispatcher的executed方法
client.dispatcher().executed(this);
// 3、通过拦截器链获取http响应Response
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
// 4、结束请求,从同步请求队列中移除该Call
client.dispatcher().finished(this);
}
}
在execute()方法中主要完成了以下功能:
- 首先,判断该请求是否被执行,一个Call智能执行一次
- 其次,调用OkHttpClient.dispatcher对象的executed方法将请求放入到同步请求队列中。
- 然后,通过okhttp中的拦截器链获取http响应
- 最后,通过调用OkHttpClient.dispatcher对象的finished()方法结束请求(这个将在下文中Dispatcher分发器中具体分析)
在同步请求中,最终通过RealCall.getResponseWithInterceptorChain()方法获取响应,通过这个方法的名字也能知道该方法的作用:通过拦截器链获取响应。
核心功能:Dispatcher分发器。
这个将在后续的章节中做具体的分析,这里只需要知道同步异步请求最终都是在Dispatcher请求分发器中完成的即可
核心功能:Interceptor拦截器。
这个将在后续的章节中做具体的分析,这里只需要知道获取Http响应是通过Intercepter拦截器中完成的即可。
enqueue异步请求
RealCall.enqueue(Callback)
@Override
public void enqueue(Callback responseCallback) {
// 1、首先判断该Call是否执行
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
// 创建一个AsyncCall对象,将对象放入到异步队列中
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
在enqueue()方法中主要完成了以下功能:
- 首先,判断该请求是否被执行,一个Call只能执行一次
- 然后,调用OkHttpClient.dispatcher对象的enqueue()方法将请求添加到异步请求队列中被执行。
在上面enqueue方法中,最后执行client.dispatcher().enqueue(new AsyncCall(responseCallback))时,创建了一个AsyncCall对象。
这里需要对这个AsyncCall做进一步分析。
AsyncCall
AsyncCall是RealCall的一个内部类,AsyncCall继承自NamedRunnable,而NamedRunnable实现了Runnable接口,所以AsyncCall主要作用就是实现Runnable接口。
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 {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
NamedRunnable是一个抽象类,实现了Runnable接口,在该类中定义了一个抽象方法execute(),主要就是实现具体的异步操作,这里对该类就不做过多解释了。
接下来主要看下NamedRunnable的子类AsyncCall
AsyncCall
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() {
boolean signalledCallback = false;
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) {
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);
}
}
}
在AsyncCall的execute方法中主要作用就两个:
-
通过RealCall.getResponseWithInterceptorChain()方法获取请求响应,这个和同步请求方法中调用的是同一个方法。
-
在获取网络响应后,通过调动 client.dispatcher().finished(this)将该请求从异步请求队列中移除,表示该请求已经处理完,并让线程池执行其他异步等待队列中的请求。
看到这里想必你就清楚了,异步处理流程和上文中RealCall.execute()同步请求是完全一样的,唯一不同的就是一个是在主线程中完成的,一个是在子线程(AsyncCall)中完成的。
关于Dispatcher分发器和Interceptor拦截器将在下面章节中具体介绍。