前言
本篇源码分析介绍的是同步请求和异步请求的执行流程以及调度器的相关知识。如果需要学习拦截器链相关知识的,可以直接跳过本篇直接到下一篇进行学习。
基本使用方法
首先要明确一点,okhttp
使用的是 Builder 模式来进行我们网络请求的各种参数设置的,接下来我们先举个简单的例子来看看 okhttp
的同步请求和异步请求是如何使用的。我们首先来看看同步请求的例子:
同步请求举例
/**
* 同步请求的简单举例
*/
public static void SyncRequest(){
new Thread(new Runnable() {
@Override
public void run() {
// 1. 构建 OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.readTimeout(8, TimeUnit.SECONDS)
.connectTimeout(8, TimeUnit.SECONDS)
.build();
// 2. 构建 Request 对象
Request request = new Request.Builder()
.get()
.url("https://www.baidu.com")
.build();
try {
// 3. 通过 newCall 方法创建 Call 对象
Call call = client.newCall(request);
//4. 通过调用 call 的 execute 方法执行同步请求
Response response = call.execute();
Log.d(TAG, response.body().string());
}catch (IOException e){
e.printStackTrace();
}
}
}).start();
}
这里我们只简要地进行一个设置,主要是说明使用步骤。
- 首先,我们要构造出一个
OkHttpClient
对象,它可以说是我们整个流程的一个核心,我们通过 Builder 模式对它进行参数设置,如读写超时、连接超时时间和缓存设置等,这里我只简单地对读超时和连接超时进行了设置。 - 其次,我们需要构造出
Request
对象,它用于设置我们的请求参数,同样它也是通过 Builder 模式进行创建的。 - 构建完
Request
对象后,我们就可以调用OkHttpClient
的newCall
方法,这个方法会将Request
对象封装成一个Call
类型的对象。 - 最后就是通过调用
Call
的execute
方法来执行同步请求了,我们这里通过一个Response
类型的变量来接收请求得到的数据。
在这里我们需要注意一点的是,我们的同步请求是放在子线程中去执行的,具体原因很简单:同步请求在没有得到响应的时候,会阻塞线程,而我们的主线程是不允许有耗时操作的,所以我们在这里需要开辟一个子线程来执行这个耗时操作。
讲完了同步请求的流程步骤,接下来我们来看看异步请求的例子。
异步请求举例
/**
* 异步请求的简单举例
*/
public static void AsyncRequest(){
// 1. 构建 OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.readTimeout(8, TimeUnit.SECONDS)
.connectTimeout(8, TimeUnit.SECONDS)
.build();
// 2. 构建 Request 对象
final Request request = new Request.Builder()
.get()
.url("https://www.baidu.com")
.build();
// 3. 通过 newCall 方法创建 Call 对象
Call call = client.newCall(request);
// 4. 通过调用 call 的 enqueue 方法执行异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, response.body().string());
}
});
}
可以看到,异步请求和同步请求的设置其实有非常多的相似之处,它们的唯一不同点在于第4步的调用方法上,同步方法调用的是 execute
,而异步调用的则是 enqueue
方法。enqueue
方法会接收一个 Callback
对象,这个对象会回调两个关键方法:onFailure
和 onResponse
。onFailure
会在我们请求失败的时候进行回调,而 onResponse
方法则是在得到响应后进行回调。
这里同样需要注意一点,所谓的异步,也就是不会阻塞我们的线程的意思,所以它实际上是在内部自己开辟了一个子线程来接收回调方法的,所以我们的 onFaliure
方法和 onResponse
方法都是位于我们的子线程中,在我们需要执行 UI 的相关操作时,应当先在方法内部切换回主线程进行操作。
接下来我们通过一张图来对同步和异步的使用做一个简单的总结:
![](https://img-blog.csdnimg.cn/20190128153904144.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MTgyMTI1,size_16,color_FFFFFF,t_70)
说完了 okhttp
两种基本的使用方法,接下来我们便要从执行流程来一步步分析 okhttp
的源码了。
源码分析
本着先易后难的原则,笔者会首先对同步请求的执行流程进行分析,然后再对异步请求的执行流程进行分析,由于 okhttp
的源码中涉及的重点如调度器器和拦截器等都是非常大的一块知识,所以笔者会将执行的流程顺序进行梳理,然后在梳理完毕之后再介绍调度器器和拦截器等的知识点。接下来我们来看看同步请求的执行流程。
同步请求的执行流程
通过上面的讲解,我们知道了,在同步请求的时候,我们首先会通过 OkHttpClient.Builder()
来对 OkhttpClient
对象进行构造,我们首先来看看 Builder
里面是什么:
public Builder() {
dispatcher = new Dispatcher(); // 调度器
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool(); // 连接池
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000; // 连接超时时间
readTimeout = 10_000; // 读超时时间
writeTimeout = 10_000; // 写超时时间
pingInterval = 0;
}
Builder
是 OkHttpClient
的一个静态内部类,可以看到它的构造方法初始化了非常非常多的成员变量,我们可以通过调用相应的方法来设置这些成员变量的值。然后通过调用 build
方法,就可以返回一个构造完毕的 OkHttpClient
的对象了,build
的源码如下所示:
public OkHttpClient build() {
return new OkHttpClient(this);
}
由于这个方法是 Builder
类中的方法,所以 this
所指的毫无疑问就是我们构造好的 Builder
对象了,通过传入这个构造好的对象,我们就可以构造出我们所需的 OkhttpClient
对象了。
好了,在了解完 OkHttpClient
对象是如何构造出来之后,第二步就是构造 Request
对象了,它也是使用的 Builder 模式构建对象的,所以我们也来看看它的 Builder
里面有什么:
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
可以看到在它的内部的 Builder
构造方法中,默认的请求方法是 get
请求,然后还会新建一个请求头。最后也是通过 build
方法构造出我们需要的 Request
对象。
构造完 Request
对象后,接下来就到了我们的第三部,将 Request
对象封装成 Call
对象。它是通过调用 OkHttpClient
的 newCall
方法实现的,我们来看看这个方法里面做了什么:
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
在这里我们可以看到,这个方法返回的是 RealCall
的 newRealCall
方法,在这里需要先说明,Call
是一个接口,它的实现类就是 RealCall
,我们接下来来看看 RealCall
的 newRealCall
方法做了什么吧!
static RealCall