OkHttp3系列
OkHttp是一个很常用的网络请求库,支持HTTPS。缓存、异步/同步请求。协议类型是Http/1.0,Http/1.1,SPDY,Http/2.0,WebSocket。
Http/1.0 每一次请求创建一个TCP连接。Http/1.1 起,默认使用Keep-alive机制建立长连接,但如果客户端发送多个并行请求则必须建立多个TCP连接。Http/2.0 中多个并行请求在一个TCP上完成,还有请求压缩等特性。
网络传输使用的是封装的Socket,而不是URLConnection,数据读写使用的 NIO(Okio)。SPDY协议类似于HTTP,但旨在缩短网页的加载时间和提高安全性。SPDY协议通过压缩、多路复用和优先级来缩短加载时间。Retrofit也是基于okhttp,因此我们不能局限于只会使用。
OkHttp有如下特性:
1)支持HTTP2,对一台机器的所有请求共享同一个socket连接(同域名下所有通信都在单个连接上完成,消除了因多个连接而带来的延时和内存消耗)。
2)内置连接池,支持连接复用,减少请求延迟
3)透明的GZIP压缩减少响应数据的大小
4)通过缓存避免重复请求
5)失败时自动重连,自动重定向
本文解析的 OkHttp 版本是 3.9.0 !!!
本篇文章介绍OkHttp执行同步和异步请求的流程及区别,先看看大致流程图:
从上面的图我们可以看到,要进行同步或异步请求,要先准备好三个对象:OkHttpClient、Request、RealCall。然后执行同步方法execute()或异步方法 enqueue(),最终都是通过getResponseWithInterceptorChain()获取响应,下篇文章对getResponseWithInterceptorChain()进行讲解。
我们先从基本使用作为入口:
// 构建okHttpClient,相当于请求的客户端,Builder设计模式
OkHttpClient client = new OkHttpClient();
// 构建一个请求体,同样也是Builder设计模式
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
// 生成一个Call对象(该对象是接口类型,后面会说), 然后调用execute执行,返回一个Response
Response response = client.newCall(request).execute();
以上就是一个简单的同步请求示例代码
1. 通过Builder模式创建OkHttpClient对象和Request对象
2. 调用OkHttpClient的newCall方法,获取一个Call对象,参数是Request
3. 调用execute方法获取一个Respone
同步请求流程:
我们先来看看 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 @Nullable SSLSocketFactory sslSocketFactory;
final @Nullable 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 connectTimeout;
final int readTimeout;
final int writeTimeout;
final int pingInterval;
• interceptors:拦截器,也是OkHttp的核心部分,后面下篇文章会将,链接在最后。
• Cache:请求缓存,只缓存 GET 请求。
• Dispatcher:分发器,下面会提及,主要是一个请求的控制器,维护三个请求队列,对请求进行管理和执行等。
• protocols:网络协议类,可以自己配置协议,但是必须包含 http/1.1,且不能包含 http/1.0,并且会默认移除 spdy/3.1,因为不会长久支持 spdy/3.1协议。
public enum Protocol {
// 一种过时的且默认不支持持久socket(长连接)的明文帧
HTTP_1_0("http/1.0"),
// 一种包含长连接的明文帧
HTTP_1_1("http/1.1"),
/**
* Chromium的二进制框架协议,包括头压缩、在同一个套接字上多路复用多个请求和服务器推送。OkHttp已经放弃了对 SPDY的支持,更倾向于 HTTP_2。
*/
SPDY_3("spdy/3.1"),
// IETF的二进制框架协议,包括头压缩、在同一个套接字上多路复用多个请求和服务器推送。
HTTP_2("h2");
}
• connectionSpecs:指定HTTP流量通过的套接字连接的配置。
• eventListenerFactory:请求事件的侦听器。扩继承这个类来监视应用程序HTTP调用的数量、大小和持续时间。但在OkHttp 3.9,这个特性是不稳定的:API可能会改变。所有的start/connect/ acquisition事件最终都会接收到一个匹配的end/release事件,要么成功(非空参数),要么失败(非空可抛出)。
请求事件排序:requestHeaders→requestBody→responseHeaders→responseBody
所有事件方法都必须快速执行,不能有外部锁定,不能抛出异常。具体可另行查看。
• proxySelector:设置全局的代理,通过继承该类,设置具体代理的类型、地址和端口。
• cookieJar:向传出的HTTP请求添加cookie,收到的HTTP返回数据的cookie处理。
• socketFactory:顾名思义,创建socket的工厂类。
• hostnameVerifier:用于验证 URL 与主机名是否匹配。
• connectionPool:管理HTTP和HTTP/2连接的重用,以减少网络延迟。相同的地址的HTTP请求可能共享一个连接。该类实现了连接的策略,以便将来使用。
• connectTimeout :连接超时,默认10s,可以自己设置
• readTimeout :读取超时,同上;
• writeTimeout = 写入超时,同上;
。。。。。省略部分。。。。。还有一些重要的后面会讲到~~
Request
接下来Request对象有哪些属性
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Object tag;
private volatile CacheControl cacheControl; // Lazily initialized.
}
都是一些大家比较能理解的,这里就不赘述了。
同步请求中,在创建一个OkHttpClient对象和一个Request对象后,用 OkHttpClient 和 Request 创建一个RealCall对象(RealCall实现Call接口),接着调用RealCall的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);
}
}
先通过executed的值判断是否执行过,说明同一个Call对象只允许执行一次,执行多次则抛出异常。接着调用OkHttpClient的dispatcher()方法获得Dispatcher对象,并执行其executed(RealCall)方法。
Dispatcher类:
/**
* Policy on when async requests are executed.
*
* <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.
*/
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;
}
public Dispatcher() {
}
...
}
Dispatcher类是执行 同步/异步 请求的控制类,内部有readyAsyncCalls、runningAsyncCalls、runningSyncCalls共三个队列。
①runningAsyncCalls用来存储正在执行的异步请求,有最大值maxRequest限制,默认64个;
②readyAsyncCalls用来存放等待的异步请求,也就是当runningAsyncCalls的尺寸达到maxRequest时,新的异步请求就会被放到readyAsyncCalls队列中等待;
③runningSyncCalls用来存储正在执行的同步请求。
Dispatcher内部有一个默认的ExecutorService类(继承自Executor),由它的实例化方法可以看出它其实是个线程池,用来执行异步请求,其默认参数如下:
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;
}
该线程池与Android下的Executors.newCachedThreadPool()比较类似,无任务上限,自动回收闲置60秒的线程,适用于大量耗时较短的任务,虽然无任务上限,但是Dispatcher对它进行了限制,最大异步请求数默认为64,同一时间同一个主机的最大请求数为5,也就是Dispatcher类中的maxRequests和maxRequestsPerHost变量。idleCallBack是Dispatcher中正在执行的同步和异步请求数量和为0时的回调。
接着我们继续看看他的execute方法:
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
Dispatcher的executed方法只是将RealCall对象添加到runningSyncCalls队列中。执行完Dispatcher的executed方法后,RealCall对象就调用getResponseWithInterceptorChain()方法执行请求,
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
}
我们可以看到,这里创建一个集合,然后放入很多拦截器对象,此处使用了责任链设计模式,在每个拦截器中都做了相应的处理。调用chain.proceed方法获取响应(下篇文章会讲)。
结束后调用dispatcher的finished(RealCall)方法:
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> 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();
}
}
这里有两个finished方法,一个是同步请求(即Call接口的实现类RealCall对象)结束时调用的,一个是异步请求(即AsyncCall对象)结束时调用的,但最终都重载到一个finished方法,区别就是第三个参数promoteCalls不同,导致执行的行为不同。
这里我们是同步请求,在finished方法中,将当前请求从runningSyncCalls移除,移除失败则抛出异常,同步请求时promoteCalls参数为false,异步请求时为true,所以这里不执行promoteCalls()方法,然后计算当前执行的同步和异步请求之和为0且idleCallback不为空则执行回调。
异步请求流程
// 构建okHttpClient,相当于请求的客户端,Builder设计模式
OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
// 构建一个请求体,同样也是Builder设计模式
Request request = new Request.Builder().url("http://www.baidu.com").build();
// 生成一个Call对象,该对象是接口类型,后面会说
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
和同步请求的区别只有最后一步,异步请求调用的是enqueue(Callback),
enqueue(Callback)方法如下:
@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));
}
同样请求只能执行一次,然后创建当前RealCall对象的内部类AsyncCall对象,调用Dispatcher的enqueue(AsyncCall)方法
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) <
maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
如果当前异步请求数没超过maxRequests(默认64)且同一个主机请求数不超过maxRequestsPerHost(默认5),dispatcher将该AsyncCall请求添加到runningAsyncCalls队列,并交给线程池 executorService 执行,否则放入到readyAsyncCalls等待队列。
AsyncCall继承自NamedRunnable,NamedRunnable继承自Runnable,当线程池执行AsyncCall时,会执行NamedRunnable的run()方法,
public abstract class NamedRunnable implements Runnable {
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
// 调用抽象方法execute()
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
在NamedRunnable的run()方法中调用了抽象方法execute(),该方法由AsyncCall实现,所以会执行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);
}
}
还是调用getResponseWithInterceptorCahin()方法获取响应,最后调用dispatcher的finished()方法:
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
private <T> void finished(Deque<T> 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();
}
}
依然从runningAsyncCalls移除当前的异步请求Call,可以看到这里和同步请求时传入的参数不同,promoteCalls为true,执行promoteCalls()方法,然后判断当前正在执行的同步和异步请求之和是否为0,调用回调。看一下promoteCalls()方法:
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
如果当前异步请求数量已达到maxRequests,则返回;如果readyAsyncCalls为空,返回;否则,遍历readyAsyncCalls队列(等待队列),如果请求的同一主机的请求数不超过maxRequestsPerHost,则将其从readyAsyncCalls队列移除,加入到runningAsyncCalls队列,并执行请求。
同步请求和异步请求的大致流程就是这样,可以看到,同步请求只是将请求添加到队列,是由主线程进行控制的,一个接一个执行,异步请求是由dispatcher进行控制的,只要不超过限制就执行请求。
参考文章https://www.jianshu.com/p/65c423d6a8eb
下篇文章对getResponseWithInterceptorChain()方法进行解读,其要点是拦截器,也是okhttp的核心!
喜欢的话点个赞,是对我最大的支持~~