OkHttp3.7源码分析文章列表如下:
拆轮子:OkHttp 的源码解析(一):概述
拆轮子:OkHttp 的源码解析(二):流程分析
拆轮子:OkHttp 的源码解析(三):任务分发器(Dispatcher)
拆轮子:OkHttp 的源码解析(四):拦截器(Interceptor)
1、OkHttp 的基本使用
用我们最常用的异步调用来分析:
OkHttpClient okHttpClient = new OkHttpClient(); // 第一行代码
Request request = new Request.Builder().url("url").build(); // 第二行代码
Call call = okHttpClient.newCall(request); // 第三行代码
call.enqueue(new Callback() { // 第四行代码
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) {
}
});
2、第一行代码分析
主要有关的类是 OkHttpClient
:
OkHttpClient okHttpClient = new OkHttpClient();
该代码作用是生成一个 OkHttpClient 对象,配置一些请求参数。
当我们 new OkHttpClient() 的时候肯定调用构造方法,先看下构造方法:
public OkHttpClient() {
this(new Builder());
}
它这里直接调用的是带参数的非 public 的构造方法:
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
······
}
下面很长不写了,反正就是配置了一些参数。但是我们把鼠标往上拉,还会看到一串很长的代码:
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.HTTP_1_1);
static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);
static {
Internal.instance = new Internal() {
@Override public void addLenient(Headers.Builder builder, String line) {
builder.addLenient(line);
}
@Override public void addLenient(Headers.Builder builder, String name, String value) {
builder.addLenient(name, value);
}
······
@Override public Call newWebSocketCall(OkHttpClient client, Request originalRequest) {
return new RealCall(client, originalRequest, true);
}
};
}
我们都知道在 Java 中被 static 修饰的成员变量和成员方法独立于该类的任何对象,被类的所有实例共享。
上面源码中为什么要这么做呢?
官方文档中有这样一个解释:
Escalate internal APIs in {@code okhttp3} so they can be used from OkHttp's implementation
packages. The only implementation of this interface is in {@link OkHttpClient}.
这也是一种封装,Internal 的唯一实现在 OkHttpClient 中,OkHttpClient 通过这种方式暴露其 API 给外部类使用。
OkHttpClient 这个类后面还有用到后面再说,我们先看到这,这行代码其实很简单就是生成了一个 OkHttpClient 的对象。
3、第二行代码分析
主要有关的类是 Request
:
Request request = new Request.Builder().url("url").build();
这行代码的作用使用链式调用生成一个 Request 对象,每一个HTTP请求包含一个URL、一个方法(GET或POST或其他)、一些HTTP头。请求还可能包含一个特定内容类型的数据类的主体部分。
将其拆分我们一个一个看:
Request.Builder builder = new Request.Builder();
Request.Builder url = builder.url("url");
Request request = url.build();
3.1 对于 new Request.Builder() 分析
Builder 是 Request 的一个内部类。当我们调用:
Request.Builder builder = new Request.Builder();
我们查看源码看是怎么走的:
public static class Builder {
HttpUrl url;
String method;
Headers.Builder headers;
RequestBody body;
Object tag;
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
······
}
从源码中可以看出,如果我们未设置请求方式,直接调用 Builder 的构造方法时,默认请求方式是 "GET"。
3.2 对于 builder.url("url") 分析
public static class Builder {
······
/**
* Sets the URL target of this request.
*
* @throws IllegalArgumentException if {@code url} is not a valid HTTP or HTTPS URL. Avoid this
* exception by calling {@link HttpUrl#parse}; it returns null for invalid URLs.
*/
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);
}
HttpUrl parsed = HttpUrl.parse(url);
if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
return url(parsed);
}
······
}
从源码中可以看到,如果我们传入一个字符串的 url,如果字符串为空会抛出一个空指针异常;不为空则判断是 http 请求还是 https 请求。
3.3 对于 url.build() 分析
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
此处也做了一个 url 的非空判断,然后生成一个 Request 对象并将其返回,我们在看下 Request 的构造方法。
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
4、第三行代码分析
Call call = okHttpClient.newCall(request);
这行代码使用上面生成的 OkHttpClient 对象来调用它的 newCall 方法,将前面的 Request 请求对象传入,所有逻辑就在 newCall( ) 方法中。
我们又回到了 OkHttpClient 类:
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
这里涉及到连个新类 Call 和 RealCall,看名字就知道他们两个有关系。
先看 Call,我删除所有的注释,如下:
package okhttp3;
import java.io.IOException;
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);
}
}
原来 Call 只是一个接口,Cloneable 这个接口是个空的,显然 RealCall 是它的具体实现类。
原来我们执行网络请求的异步 enqueue
和同步 execute
操作都定义在这。
现在跳转到 RealCall 这个实现类,在上面我们看到传入了三个参数,点击去看下:
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
// TODO(jwilson): this is unsafe publication and not threadsafe.
this.eventListener = eventListenerFactory.create(this);
}
5、最后一行代码
终于到了最后一行代码,请求服务器失败成功和返回值都在这里,又分为同步和异步。
同步请求
call.execute();
点击 execute()
方法的源码如下:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
先判断请求 Call
是否已经执行了,如果执行了则抛出异常,然后继续执行, client.dispatcher().executed(this)
点击进去代码如下:
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
可以看出同步请求就是把生成的 Call
插入同步请求的队列 runningSyncCalls
中。
Response result = getResponseWithInterceptorChain();
返回的 result 就是网络请求的返回数据。
最后调用 finishied
方法将 call
从同步队列中删除。
异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) {
}
}
源码如下:
@Override void enqueue(Callback responseCallback, boolean forWebSocket) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}
可以看出,不管是异步还是同步,检查这个 call
是否已经被执行了,每个 call
只能被执行一次,如果想要一个完全一样的 call
,可以利用 call#clone
方法进行克隆。
同样先置标志位,然后将封装的一个执行体放到异步执行队列中。这里面引入了一个新的类AsyncCall
,这个类继承于NamedRunnable
,实现了Runnable
接口。NamedRunnable
可以给当前的线程设置名字,并且用模板方法将线程的执行体放到了execute
方法中。
两种请求的方法
enqueue/execute
方法中涉及好几个新的类和方法,如下:
-
同步
execute
:- captureCallStackTrace()
- dispatcher()
- getResponseWithInterceptorChain()
-
异步
enqueue
:- captureCallStackTrace()
- dispatcher()
- AsyncCall
我们看到不管是同步还是异步都会调用两个相同的方法 captureCallStackTrace()
和 dispatcher()
,先看下 captureCallStackTrace()
源码:
private void captureCallStackTrace() {
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
}
其实,我们根据方法的名字就知道什么意思,Stack:栈,Trace:追踪 ,是用来跟踪调用栈的信息的,不去深究了。
对于 dispatcher()
,它涉及到一个新的类 Dispatcher
,这个类有点复杂,它是一个核心重点类。在下篇文章中详细说。