RxJava源码的基础部分分析的差不多,后续如果有新的内容话,会继续的补充。从今天开始,我们来看看OkHttp的相关源码。OkHttp的源码过于复杂,涉及到的方面非常的多,本系列文章目的是打通Okhttp的整个执行流程,不对某一个细节重点分析。
本篇文章是本系列文章的第一篇,我们先从最简单的Okhttp使用入手,进而分析Okhttp两种请求方式的流程。
1. 同步请求
同步请求的重点在于同步二字,顾名思义,执行同步请求,不会单独的开一个线程,所以在进行网络请求时,当前线程会阻塞在这里。
我们来简单的看看,怎么进行一个同步请求:
private final OkHttpClient mOkHttpClient = new OkHttpClient.Builder().readTimeout(50, TimeUnit.SECONDS).build();
private void syncRequest() {
Request request = new Request.Builder().url("http://www.baidu.com").get().build();
Call call = mOkHttpClient.newCall(request);
try {
Response response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
}
同步请求的执行是调用了call
的execute
方法。但是在调用execute
方法之前,还要进行一些准备操作。
(1). OkHttpClient的创建
OkHttpClient
是一个非常基础的类,在使用OkHttp来进行网络请求时,我们必须先创建OkHttpClient
的对象。在这个类里面,我们会配置很多的参数,比如超时连接时间、拦截器等等。我们来详细的看一看:
private final OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
.readTimeout(50, TimeUnit.SECONDS)
.build();
上面就是一个非常简单的创建列子。现在我们来从源码的角度来看看OkHttpClient
究竟给我们配置那些参数。
我们知道OkHttpClient
是通过建造者模式来创建的,我们先来看看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;
}
额,构造方法里面初始化我们很多的东西,这里我们需要看两个东西:
dispatcher = new Dispatcher();
connectionPool = new ConnectionPool();
Dispatcher
是一个分发器,我们所有的Request
请求都是通过Dispatcher
分发的。在后续的文章,我会详细讲解这个类。本文就不对它做过多的解释。
ConnectionPool
是一个连接池,很多的请求连接都由这个类管理,比如有些连接需要重用,都由这个类来管理的。这个类跟Dispatcher
类一样,后续会详细的介绍这个类。
最后就是调用Builder
的build
方法来真正创建OkHttpClient
对象。
(2). Request的创建
Request
的创建方式跟 OkHttpClient
都是通过Builder
方法来创建的,在创建Request
的对象时,我们会初始化很多东西,比如请求方式(get或者post)、请求的URL等等。这里就不详细的分析了。
(3).Call的创建
我们调用OkHttpClient
的newCall
方法来创建一个Call
对象,我们来看看Call
对象的创建过程。
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
Call
是一个接口,它的唯一实现类是RealCall
类,所以我们可以看到在Call
方法里面又调用了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;
}
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
归根结底,还是将我们创建好的OkHttpClient
对象和Request
对象传递到RealCall方法里面了。
(4).调用Call的execute方法
同步请求的最后一步操作就是调用Call
的execute
方法,我们来看看整个execute
方法时怎么执行的。
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
我们来重点分析几个点。首先:
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
从这里看的出来,每一个Call只能被执行一次,当第二次调用时,会抛出IllegalStateException
异常。
然后就是这一对:
client.dispatcher().executed(this);
client.dispatcher().finished(this);
在正式进行网络请求之前,会调用Dispatcher
的executed
方法来将一个Call
对象放在一个队列;在正式进行网络请求之后,会调用Dispatcher
的finished
方法将这个Call
对象从队列中移除。
但是,这里,我们没有看到网络请求的步骤啊?究竟是哪一步进行了网络请求呢?没错,就是这一步。
Response result = getResponseWithInterceptorChain();
getResponseWithInterceptorChain
方法通过调用拦截器链的每一个拦截器的proceed
方法,最终返回Response
对象,就是我们请求的数据。拦截器部分,在本篇文章不进行解释,后续会单独解释OkHttp的拦截器。
这就是,整个同步请求的执行过程,是不是非常的简单呢?接下来,我们来看看异步请求的。
2. 异步请求
异步请求的准备工作跟同步请求差不多,都是先创建OkHttpClient对象、然后创建Request对象,再创建Call对象,最后调用相应的方法来执行执行这个请求,异步请求调用的是enqueue
方法。我们来看看enqueue
方法:
@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));
}
前面部分,我们可以不看,重点在最后一行。我们先来看看Dispatcher
的enqueue
方法:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
首先,先判断当前异步请求的数目是否超过我们设置的值,这里默认值是64,其次在判断当前的请求的主机是否超过了5个,都不超过的话,那么就将当前的Call添加到异步队列中,然后将当前的Call
对象提交到线程池中去执行;如果超过的话,那么就将当前的Call
对象放入等待队列中去。
我们知道,在同步请求中,是通过调用getResponseWithInterceptorChain
方法来进行网络请求的,但是在这个异步请求过程中,我们并没有发现getResponseWithInterceptorChain
方法的调用,难道是我们分析错了吗?并没有,我们需要在AsyncCall
里面来寻找答案。
我们先来看看AsyncCall
的结构:
final class AsyncCall extends NamedRunnable {
}
AsyncCall
是继承于NamedRunnable
的,我们再去看看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
中,我们发现在run
方法中调用了execute
方法,最后我们回到了AsyncCall
中来,不过这次,我们只需要关注execute
方法就行了。
@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);
}
}
}
还是熟悉的味道,跟同步请求的差不多。但是这里我们需要注意的是,从这里可以看出,Callback
的onFailure
方法和onResponse
方法都是在子线程中执行的,并没有通过Handler将消息传递到主线程。这里需要特别注意。
3.总结
OkHttp两种请求方式的执行流程是比较简单,现在对它做一个简单的总结。
1.同步请求和异步请求的在执行,都必须做同样的步骤来准备。包括OkHttpClient
的创建,Request
的创建,Call
的创建。
2.同步请求是调用execute
方法来执行,在execute
方法中,通过调用getResponseWithInterceptorChain
方法来真正进行网络请求,同时每一个Call只能执行一次。
3.异步请求是通过调用enqueue
方法,最后提交到一个线程里面执行的。这里需要的注意的是Callback
的onFailure
方法和onResponse
方法都是在子线程执行的。