0.前言
OkHttp,相信大家都熟悉,就连retrofit内部也是基于它实现的,是一个重量级的网略请求框架。其支持同步请求和异步请求,核心的东西是内部维护了一个拦截器和线程池,用于存放网略请求和拦截,其内部实现的方法,真的是妙不可言,其中的妙处何在?让我们来一步一步了解。
本系列共两篇文章:
OkHttp源码分析之(一)请求流程
OkHttp源码解析之(二)任务调度——Dispatcher和拦截器
1.使用
(1)okhttp的使用介绍还是要有的,首先当然是先导入依赖包:
implementation 'com.squareup.okhttp3:okhttp:3.6.0'
(2)使用三步曲
- OkHttpClient:
val client = OkHttpClient.Builder()
.readTimeout(READ_TIMEOUT.toLong(), TimeUnit.SECONDS)//设置读取超时时间
.writeTimeout(WRITE_TIMEOUT.toLong(), TimeUnit.SECONDS)//设置写的超时时间
.connectTimeout(CONNECT_TIMEOUT.toLong(), TimeUnit.SECONDS)//设置连接超时时间
.build()
这里简单的只是设置了下超时和读写时间
- Request
val request = Request.Builder()
.post(body)
.url(url)
.build()
使用request构造我们的请求,将请求参数传递进去。
- newCall
client.newCall(request).enqueue(myCallBack)//异步请求,无阻塞
client.newCall(request).execute();//同步请求,阻塞进程
就是这么简单,那么这几个步骤究竟干了什么呢?为什么一定要这几个步骤呢?我们不是要请求网络吗?我可以肯定的告诉你,第一步设置好连接状态相关的参数,第二步写好请求内容,第三步发起请求。接下来从源码角度上看看这三步到底做了什么。
2.源码解析
鉴于源码比较长,为避免乏味,我尽量在源码里面采用注释说明的方法来解析,然后再重点强调一下。
(1)OkHttpClient
首先是OkHttpClient,一般生成client是使用build方法,先来看看Builder()这厮干了什么
- Builder
public Builder() {
dispatcher = new Dispatcher();//初始化任务调度器——OkHttp的核心之一
protocols = DEFAULT_PROTOCOLS;//协议
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
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;
callTimeout = 0;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
可以看到,Builder方法里面对线程池和调度器、读写超时时间等进行了初始化,已备后续使用,Builder是内部定义的一个类,包含readTimeout()、writeTimeout等这些我们常用的方法,方法本身又返回Builder本身,所以可以连续几个方法调用下去。这两个方法的源码如下:
public Builder readTimeout(long timeout, TimeUnit unit) {
readTimeout = checkDuration("timeout", timeout, unit);
return this;
}
public Builder writeTimeout(long timeout, TimeUnit unit) {
writeTimeout = checkDuration("timeout", timeout, unit);
return this;
}
没错,使用了建造者模式!
返回Builder之后,又去调用了build方法创建OkHttpClient对象:
- build
public OkHttpClient build() {
return new OkHttpClient(this);
}
生成OkHttpClient对象,又把本身传递过去,看看OkHttpClient的构造方法都做了什么
final Dispatcher dispatcher;
final @Nullable Proxy proxy;
final List<Protocol> protocols;
final List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors;
final List<Interceptor> networkInterceptors;
final EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector;
final CookieJar cookieJar;
final @Nullable Cache cache;
final @Nullable InternalCache internalCache;
final SocketFactory socketFactory;
final SSLSocketFactory sslSocketFactory;
final CertificateChainCleaner certificateChainCleaner;
final HostnameVerifier hostnameVerifier;
final CertificatePinner certificatePinner;
final Authenticator proxyAuthenticator;
final Authenticator authenticator;
final ConnectionPool connectionPool;
final Dns dns;
final boolean followSslRedirects;
final boolean followRedirects;
final boolean retryOnConnectionFailure;
final int callTimeout;
final int connectTimeout;
final int readTimeout;
final int writeTimeout;
final int pingInterval;
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
//从builder里面取出参数进行赋值,值为刚才初始化时的值或返回Builder之后调用readTimeout等方法赋值之后的值
this.dispatcher = builder.dispatcher;//
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if (sslSocketFactory != null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.callTimeout = builder.callTimeout;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}
代码不多,主要就是对OkHttpClient需要用到的值进行初始化。这样一来,client就获取到了。
(2)Request
获取到client之后,接下来第二步就是获取request。request,请求,顾名思义,即网络请求的内容(怎么说呢,如果有javaWeb基础的就好理解了,相当于网络请求中的请求内容),看看它做了什么。
- Builder
和client类似,首先也是用Builder()获取Builder:
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
代码只有简简单单的四行,设置默认请求为get请求(你可以在获取Builder之后调用post()方法更改为post请求)。
然后是build()方法
- build()
public Request build() {
if (url == null) throw new IllegalStateException("url == null");//空异常
return new Request(this);
}
也是传递自身为参数,返回Request对象:
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Map<Class<?>, Object> tags;
private volatile @Nullable CacheControl cacheControl; // Lazily initialized.
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tags = Util.immutableMap(builder.tags);
}
同样,也是初始化。
由此可知,这两步主要就是为了初始化参数值,为以后做准备。
(3)newCall
client里面的newCall方法,才是真正执行网络请求的方法
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
这厮有点懒,把任务丢给RealCall去做。看看RealCall又干了什么:
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);//生成RealCall 对象
call.eventListener = client.eventListenerFactory().create(call);//赋值事件监听
return call;
}
如注释所言,其中 client.eventListenerFactory()的值在前两步初始化client时已经附值,这里仅仅是调用它的create方法而已。
方法最后调用的是RealCall的对象引用,然后就可以用它进行网络请求啦!
这里,分为两种情况:一种是同步请求,另一种是异步请求。
这里分别对应这两种情况进行源码解析:
同步请求execute()
先来看看同步请求,刚才说了,client比较懒,把事情丢给RealCall去做,所以这里,我们要看RealCall里面的execute()方法:
@Override public Response execute() throws IOException {
//锁住
synchronized (this) {
//避免重复
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace(); //处理异常
timeout.enter();
eventListener.callStart(this);//开启监听(每当调用execute和enqueue方法)
try {
client.dispatcher().executed(this);//调用dispatcher方法将同步请求添加到同步请求队列中
Response result = getResponseWithInterceptorChain();//返回处理结果
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);//回收同步请求(从同步请求队列中移除)
}
}
//Dispatcher类中
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);//添加到同步请求队列中
}
dispatcher内维护了同步请求队列、异步请求队列和异步请求等待队列,getResponseWithInterceptorChain这里稍后再详细深入解析,这里先了解下大概流程。方法最后调用 client.dispatcher().finished(this)回收同步请求,:
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, 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();
}
}
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 (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
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;
}
就是这么简单,总结起来就干了两件事:保存同步请求、移除同步请求。
接下来看看它的异步请求
异步请求enqueue(callBack)
同样,异步请求也在RealCall类里面,先来看看它的代码:
@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));//将异步请求添加至队列中,AsyncCall为内部定义的runnable类
}
与execute方法类似,最后调用Dispatcher方法将请求添加至异步请求队列中,代码如下:
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);//加入队列
}
promoteAndExecute();
}
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 (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
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;
}
//executorService为线程池,定义了线程池大小以及存活时间
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
//贴出ThreadPoolExecutor的构造方法,对数值进行初始化
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
方法执行与execute方法类似,最后是调用AsyncCall类的executeOn方法执行,AsyncCall是个子线程,网略请求放在了子线程,即异步请求。
总结起来,enqueue几件事:加入等待执行的队列中、加入可执行的队列中、执行请求
接下来看看具体的网略请求:
- AsyncCall
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;//responseCallback用于回调请求结果,即我们一开始传进来的Callback ,是不是很熟悉?
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
/**
* 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);//执行runnable
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
//失败回调
eventListener.callFailed(RealCall.this, 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;
timeout.enter();
try {
Response response = getResponseWithInterceptorChain();//核心之一,拦截器链
//判断retryAndFollowUpInterceptor——重定向重置拦截器释放取消了
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));//如果取消了请求,回调请求失败
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);//请求成功,就调用response方法
}
} catch (IOException e) {
e = timeoutExit(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);//移除请求
}
}
}
有关拦截器的分析,由于篇幅关系,这里不做解释,详情请看本系列文章:OkHttp源码解析之(二)任务调度——Dispatcher和拦截器
可以看到,AsyncCall做的事情也不多:执行请求、回调结果
总结
通过以上分析,我们知道OkHttp的网略请求过程遵循“三步曲”,请求又分为同步请求和异步请求,同步请求返回一个Response对象,异步请求无返回,结果通过CallBack回调返回。由于篇幅关系,本文侧重于讲解OkHttp的执行流程,让大家有个清晰的流程,那么接下来,是时候讲讲OkHttp的核心:Dispatcher和拦截器链
流程图还是要有的:
图有点长,包含了主要的步骤,慢慢看吧~~