- Okhttp在代码中的构建方式
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS)
.writeTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.addNetworkInterceptor(getCacheInterceptor()).cache(cache)//设置缓存
.addInterceptor(getHttpLoggingInterceptor())//打印日志
.build();
Request request = new Request.Builder().url("xxxx").build;
Call call = client.newCall(request);
//同步请求
Response response = call.execute();
//异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
同步和异步请求在使用OkHttp时,构建client、request、call的过程是一样的,只在最后执行请求时存在区别,内部的实现也存在区别。
- 根据构建方式,我们先来看一下Okhttp的源码
(1)先看一下OkHttpClient的内部类Builder部分代码,主要是成员变量
public static final class Builder {
Dispatcher dispatcher;//重中之重,分发器,异步请求的分发,就绪队列还是执行队列
@Nullable Proxy proxy;
List protocols;
List connectionSpecs;
final List interceptors = new ArrayList<>();
final List networkInterceptors = new ArrayList<>();
EventListener.Factory eventListenerFactory;
ProxySelector proxySelector;
CookieJar cookieJar;
@Nullable Cache cache;
@Nullable InternalCache internalCache;
SocketFactory socketFactory;
@Nullable SSLSocketFactory sslSocketFactory;
@Nullable CertificateChainCleaner certificateChainCleaner;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;//连接池,非常重要
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int connectTimeout;
int readTimeout;
int writeTimeout;
int pingInterval;
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 okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
this.interceptors.addAll(okHttpClient.interceptors);
this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
this.eventListenerFactory = okHttpClient.eventListenerFactory;
this.proxySelector = okHttpClient.proxySelector;
this.cookieJar = okHttpClient.cookieJar;
this.internalCache = okHttpClient.internalCache;
this.cache = okHttpClient.cache;
this.socketFactory = okHttpClient.socketFactory;
this.sslSocketFactory = okHttpClient.sslSocketFactory;
this.certificateChainCleaner = okHttpClient.certificateChainCleaner;
this.hostnameVerifier = okHttpClient.hostnameVerifier;
this.certificatePinner = okHttpClient.certificatePinner;
this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
this.authenticator = okHttpClient.authenticator;
this.connectionPool = okHttpClient.connectionPool;
this.dns = okHttpClient.dns;
this.followSslRedirects = okHttpClient.followSslRedirects;
this.followRedirects = okHttpClient.followRedirects;
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;
this.connectTimeout = okHttpClient.connectTimeout;
this.readTimeout = okHttpClient.readTimeout;
this.writeTimeout = okHttpClient.writeTimeout;
this.pingInterval = okHttpClient.pingInterval;
}
}
Dispatcher dispatcher;//重中之重,分发器,异步请求的分发,就绪队列还是执行队列。同步请求,简单放入同步请求队列执行。
ConnectionPool connectionPool;//连接池,非常重要,客户端和服务器端每一个连接是一个connection,而每一个connection放入连接池中进行统一管理。(1)当请求的url是同一个时,可以复用。(2)ConnectionPool还可以管理连接的策略(打开关闭,复用等)
(2)Request同样也是Builder构建模式创建的,其中包含了请求的url,请求头,网络请求方法,请求体等等内容。
(3)Call call = client.newCall(request)生成Call请求对象。
/**
* 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 */);
}
内部实际是接口Call的实现类ReallCall执行的newRealCall方法。那么我们来看ReallCall.newReallCall做了什么。
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;
}
我们首先看下在创建RealCall的构造方法中做了哪些工作。
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
可以看到RealCall持有了前两步创建的OkHttpClient对象和Request对象,除此之外还创建了一个重定向拦截器RetryAndFollowUpInterceptor,后续我们会介绍。
上面三个步骤,无论是同步还是异步都是一样的,那么他们之间的区别就是在下面第四步
(4)同步请求
Response response = call.execute();
我们来看源码call.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);
}
}
首先在同步代码块中,判断executed,如果为true则抛出异常,否则设置为true,这说明同一个http请求只能请求一次。
client.dispatcher().executed(this);我们主要来看这一句代码,dispatcher()方法返回了一个Dispatcher对象,具体来看Dispatcher的executed方法。
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
可以看到,直接将call添加到同步请求执行队列当中。
返回execute主流程中继续看源码:
Response result = getResponseWithInterceptorChain();
通过处理拦截器链后获取到服务器的响应结果,具体后续详细介绍。
最后一步注意finally中的代码client.dispatcher().finished(this);
最终还是通过client的分发器结束移出请求call对象,追踪代码
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private void finished(Deque calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
我们可以看到将当前Call对象从同步请求执行队列中移除,并且通过runningCallsCount()方法重新计算了当前正在运行的异步和同步请求的和。同时我们可以注意到一个bool参数promoteCalls在同步请求中是false,导致promoteCalls()方法不会执行,但是在异步请求中是会执行的,这也就是同步和异步之间的一个区别。
总结一下分发器在同步请求中的作用:将同步请求添加和移除同步请求
队列
异步请求前三步和上边是一样的,只有最后一步发起请求不同,追踪源码:
@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));
}
异步请求方法enqueue中前几步依然和同步方法的execute方法相同,不同的在最后一步client.dispatcher().enqueue(new AsyncCall(responseCallback));
那么我们来跟进代码看一下:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
final class AsyncCall extends NamedRunnable
在Dispatcher类中定义了两个值,一个maxRequests为64 ,一个maxRequestsPerHost为5,当前运行的异步请求不超过64个且每个主机的请求不超过5个则可以将该异步请求添加到正在执行的异步请求队列中,并通过一个线程池去执行该Runnable。否则,就将当前请求添加到等待的异步请求队列中。
接下来我们来分析一下线程池执行该Runnable的过程,首先来看下该线程池。
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
同步方法构造单例对象executorService,然后调用了线程池的execute来运行Runnable,那实际就是执行Runnable的run方法,跟踪代码:
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
@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);
}
}
}
进来之后并没有发现run方法,那么肯定在父类中,我们来继续看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();
}
这是一个抽象类,run中的关键方法execute调用了一个抽象方法,那么实现还是在子类中,我们返回到AsyncCall 中看execute方法。
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);
}
}
}
这个方法在上面已经贴过了,为了便于分析代码跟踪流程和实际代码,这里再贴一遍。
注意:execute方法执行位置是在run方法中,已经在子线程中了。getResponseWithInterceptorChain这个有关拦截器链的方法我们同样留到后续讲解。
responseCallback.onResponse(RealCall.this, response);成功回调执行的方法。
最后finally同样执行了 client.dispatcher().finished(this);是否似曾相识,没错,同步方法时也是执行了这个方法,真的是吗?其实不是,只是同步方法的一个重载方法,两者参数一个是RealCall(同步),一个是AsyncCall(异步,这是个Runnable)。跟踪代码:
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
private void finished(Deque calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
同步方法时promoteCalls为false,而异步方法为true,其他部分和同步方法执行一致,那么我们单独来看if (promoteCalls) promoteCalls();promoteCalls()这个方法调整了我们异步请求的队列。
后续会有OkHttp中非常重要的调度器Dispatcher和拦截器的介绍。