之前项目中所有的网络请求都是基于OKHttp完成。之前也多次阅读过源码,但是都没有做过系统的整理。最近工作轻松了下来,终于有时间整理整理,在本文中分析一次OKHttp网络请求的整理流程与原理。
开始分析代码前我们先来看下OKHttp请求的时序图
[OKHttp网络请求时序图](https://www.processon.com/view/link/5e9ed982e401fd21c1845af6)
接下来我们参考时序图来分析一个完整的请求流程
OkHttlpClient客户端的构建
这里使用了构造者模式来生成OKHttpClient对象,部分代码如下
public class OkHttpClient implements Cloneable, Call.Factory {
...
public static final class Builder {
Dispatcher dispatcher;// 请求事件分发器
Proxy proxy;// 代理
List<Protocol> protocols;// 支持的协议
List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors = new ArrayList<>();// 拦截器
final List<Interceptor> networkInterceptors = new ArrayList<>();
ProxySelector proxySelector;
CookieJar cookieJar;
Cache cache;// 缓存
InternalCache internalCache;
SocketFactory socketFactory;// socket工厂,用户创建&管理socket连接
SSLSocketFactory sslSocketFactory;// ssl socket工厂,用户创建&管理socket连接
TrustRootIndex trustRootIndex;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;// 连接池
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int connectTimeout;
int readTimeout;
int writeTimeout;
.....
}
}
在Builder里进行的一些变量的初始化,在这里我们看到两个创建socket的工厂,由此便知道OKHttp是通过socket进行连接的,至于使用socket的这里就不在进行介绍。此外也初始化了一次连接池connectionPool ,使用连接池可以减少对象的反复创建,优化内存的使用。此外进行了Dispatcher 的初始化,我们来看下它的描述:
<p>Each dispatcher uses an {@link ExecutorService} to run calls internally. If you supply your
own executor, it should be able to run {@linkplain #getMaxRequests the configured maximum} number
of calls concurrently.
具体功能就是 使用一个线程池调度请求。由此我们知道了,OKHttp的网络请求是在线程池中使用的。此外大名鼎鼎的拦截器也会在此进行初始化
Request构建
Request的构建与OKHttpClient相似,同样都是使用构造者模式,部分代码如下,这里就不在做详细说明:
public final class Request {
.....
public static class Builder {
private HttpUrl url;// 请求的Url地址
private String method;// 请求方法 Get 、Post
private Headers.Builder headers; // 请求头
private RequestBody body;// 请求体,请求参数
private Object tag;
.....
}
......
}
Call的构建
Call的构建是由 okHttpClient.newCall(request)开始,我们来跟下源码,在方法调用之后返回了一个RealCall方法,RealCall中保存了当前OKHttpClient与Request,我们可以认为是对请求进行了进一步封装,方便后续操作。
public class OkHttpClient implements Cloneable, Call.Factory {
@Override public Call newCall(Request request) {
return new RealCall(this, request);
}
}
同步网络请求
在OKHttpClient与Call构建完成之后,接下来就开始进行网络请求,首先我们来分析同步请求。同步请求的入口是 call.execute,从Call的构建中我们了解到返回的是RealCall对象,我们来看下这一步做了什么:
final class RealCall implements Call {
@Override public Response execute() throws IOException {
synchronized (this) { // 利用线程锁保证线程安全
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();// 开始连接计时
transmitter.callStart();
try {
client.dispatcher().executed(this);// 将当前请求交给分发器处理
return getResponseWithInterceptorChain();// 依次经过拦截器,并返回结果
} finally {
client.dispatcher().finished(this);// 关闭请求
}
}
}
通过源码我们可以看到,请求被提交给了Dispatcher,我们看Dispatcher对同步请求进行了什么操作
public final class Dispatcher {
...
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
....
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
...
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
...
}
从源码中我们可以看到Dispatcher中有一个runningSyncCalls 队列(队列具有先进先出的特性),在执行时请求Call 被添加到了队列当中。结束时 由于我们在执行同步请求时没有给idleCallback 赋值,所以在结束时仅仅是执行了calls.remove(call))操作,将请求移除队列。看完请求的分发,我们来看下最终的结果,我们看到请求响应是由getResponseWithInterceptorChain()返回,至于里面做了什么处理,由于异步请求最终同样会执行这个方法,所以我们在下一遍文章中再做解释。至此一次同步请求就有了结果。
异步请求
通过时序图我们了解到,异步请求是由 call.enqueue()发起,我们看下源码中做了怎样的处理
final class RealCall implements Call {
...
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
...
}
最核心的代码 client.dispatcher().enqueue(new AsyncCall(responseCallback));由代码,我们知道请求被再次包装成了AsyncCall,我们来看下AsyncCall这究竟是一个什么,
final class AsyncCall extends NamedRunnable {
...
}
/**
* Runnable implementation which always sets its thread name.
*/
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();
}
是不是恍然大悟,为什么异步请求是在子线程中进行了,原来是在这里将参数封装进了一个Runnable对象中。 我们接着看,请求被提交给了Dispatcher,我们来看下里面执行了什么操作
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
.....
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
.....
promoteAndExecute();
}
....
}
通过源码我们了解到,当前请求提交之后,请求会被加入到一个预备队列当中准备执行,紧接着调用promoteAndExecute,我们先看下方法介绍
/**
- Promotes eligible calls from {@link #readyAsyncCalls} to {@link #runningAsyncCalls} and runs
- them on the executor service. Must not be called with synchronization because executing calls
- can call into user code.
- @return true if the dispatcher is currently running calls.
*/
将符合要求的请求从readyAsyncCalls 移动到 runningAsyncCalls,并且在线程池中运行他们,
不能通过同步调用,因为正在执行调用可以调用用户代码。
b@return如果调度程序当前正在运行呼叫,则为true
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);// 可执行队列
runningAsyncCalls.add(asyncCall);// 加入可执行队列
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
通过源码我们可以看出,加入正在执行队列的条件是: 当前正在执行的请求数小于最大请求数,且 请求主机数据小于允许的最大请求主机数目。当队列被加速可以执行队列之后,会在线程池中得到执行,
asyncCall.executeOn(executorService());我们接着看AsyncCall.executeOn 源码
final class AsyncCall extends NamedRunnable {
...
/**
* Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
* if the executor has been shut down by reporting the call as failed.
*/
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);
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
@Override protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();
try {
Response response = getResponseWithInterceptorChain();
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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
...
}
通过源码我们可以看到最终会调用到execute()方法,是不是很熟悉,和同步请求的execute 方法很像,同样是调用getResponseWithInterceptorChain()获取结果,只不过不是将结果直接返回,而是通过回调传值。
好了,网络请求的流程已经分析完了,如果有什么不足的地方,还望各位指出。在接下来的一篇文章中,将会介绍OkHttp拦截器的相关源码。谢谢大家