因为以前项目搭建的是 MVP 架构,由RxJava + Glide + OKHttp + Retrofit
等开源框架组合而成,以前也都是停留在使用层面上,没有深刻的研究,最近打算把它们所有攻下,尚未关注的同窗能够先关注一波,看完这个系列文章,(不论是面试仍是工做中处理问题)相信你都在知道原理的状况下,处理问题更加驾轻就熟。java
Android 图片加载框架 Glide 4.9.0 (一) 从源码的角度分析 Glide 执行流程git
Android 图片加载框架 Glide 4.9.0 (二) 从源码的角度分析 Glide 缓存策略github
从源码的角度分析 Rxjava2 的基本执行流程、线程切换原理web
从源码的角度分析 OKHttp3 (一) 同步、异步执行流程面试
从源码的角度分析 OKHttp3 (二) 拦截器的魅力json
从源码的角度分析 Retrofit 网络请求,包含 RxJava + Retrofit + OKhttp 网络请求执行流程数组
OKHttp 出至于 移动支付 Square 公司, 适用于Android
,Kotlin
和 Java
的 HTTP 客户端,我的认为就目前来讲 OKHttp 是最好用之一的网络请求框架。在网络请求中使用 OKhttp 好处也是显而易见的,好比以下几点:缓存
在介绍源码以前请先看基本的 Get
、Post
使用安全
Get 同步
public OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
复制代码
POST 异步
public final MediaType JSON = MediaType.get("application/json; charset=utf-8");
public OkHttpClient client = new OkHttpClient();
public void post(String url, String json) {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
}
复制代码
源码分析
在进入 OKHttp 源码以前先看一下各自执行的流程
同步时序图
异步时序图
代码讲解
经过上面时序图能够知道,其实不论是异步仍是同步,最后都是在 getResponseWithInterceptorChain();
函数中处理 Request
、Response
能够说 getResponseWithInterceptorChain()
这个函数是整个处理请求和响应的核心,这个咱们待会再详细讲解,下面咱们来解析 OKHttp 代码执行流程:
根据同步、异步代码示例,首先建立一个 OkHttpClient
//建立 OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient();
复制代码
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
...//省略部分代码
public OkHttpClient() {
this(new Builder());
}
public Builder newBuilder() {
return new Builder(this);
}
public static final class Builder {
Dispatcher dispatcher; //调度器
/** * 代理类,默认有三种代理模式DIRECT(直连),HTTP(http代理),SOCKS(socks代理) */
@Nullable Proxy proxy;
/** * 协议集合,协议类,用来表示使用的协议版本,好比`http/1.0,`http/1.1,`spdy/3.1,`h2等 */
List<Protocol> protocols;
/** * 链接规范,用于配置Socket链接层。对于HTTPS,还能配置安全传输层协议(TLS)版本和密码套件 */
List<ConnectionSpec> connectionSpecs;
//拦截器,能够监听、重写和重试请求等
final List<Interceptor> interceptors = new ArrayList<>();
final List<Interceptor> networkInterceptors = new ArrayList<>();
EventListener.Factory eventListenerFactory;
/** * 代理选择类,默认不使用代理,即便用直连方式,固然,咱们能够自定义配置, * 以指定URI使用某种代理,相似代理软件的PAC功能 */
ProxySelector proxySelector;
//Cookie的保存获取
CookieJar cookieJar;
/** * 缓存类,内部使用了DiskLruCache来进行管理缓存,匹配缓存的机制不只仅是根据url, * 并且会根据请求方法和请求头来验证是否能够响应缓存。此外,仅支持GET请求的缓存 */
@Nullable Cache cache;
//内置缓存
@Nullable InternalCache internalCache;
//Socket的抽象建立工厂,经过createSocket来建立Socket
SocketFactory socketFactory;
/** * 安全套接层工厂,HTTPS相关,用于建立SSLSocket。通常配置HTTPS证书信任问题都须要从这里着手。 * 对于不受信任的证书通常会提示 * javax.net.ssl.SSLHandshakeException异常。 */
@Nullable SSLSocketFactory sslSocketFactory;
/** * 证书链清洁器,HTTPS相关,用于从[Java]的TLS API构建的原始数组中统计有效的证书链, * 而后清除跟TLS握手不相关的证书,提取可信任的证书以即可以受益于证书锁机制。 */
@Nullable CertificateChainCleaner certificateChainCleaner;
/** * 主机名验证器,与HTTPS中的SSL相关,当握手时若是URL的主机名 * 不是可识别的主机,就会要求进行主机名验证 */
HostnameVerifier hostnameVerifier;
/** * 证书锁,HTTPS相关,用于约束哪些证书能够被信任,能够防止一些已知或未知 * 的中间证书机构带来的攻击行为。若是全部证书都不被信任将抛出SSLPeerUnverifiedException异常。 */
CertificatePinner certificatePinner;
/** * 身份认证器,当链接提示未受权时,能够经过从新设置请求头来响应一个 * 新的Request。状态码401表示远程服务器请求受权,407表示代理服务器请求受权。 * 该认证器在须要时会被RetryAndFollowUpInterceptor触发。 */
Authenticator proxyAuthenticator;
Authenticator authenticator;
/** * 链接池 * * 咱们一般将一个客户端和服务端和链接抽象为一个 connection, * 而每个 connection 都会被存放在 connectionPool 中,由它进行统一的管理, * 例若有一个相同的 http 请求产生时,connection 就能够获得复用 */
ConnectionPool connectionPool;
//域名解析系统
Dns dns;
//是否遵循SSL重定向
boolean followSslRedirects;
//是否重定向
boolean followRedirects;
//失败是否从新链接
boolean retryOnConnectionFailure;
//回调超时
int callTimeout;
//链接超时
int connectTimeout;
//读取超时
int readTimeout;
//写入超时
int writeTimeout;
//与WebSocket有关,为了保持长链接,咱们必须间隔一段时间发送一个ping指令进行保活;
int pingInterval;
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
/** * 代理选择类,默认不使用代理,即便用直连方式,固然,咱们能够自定义配置,以指定URI使用某种代理,相似代理软件的PAC功能 */
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;
}
....//省略部分代码
}
复制代码
经过 OkHttpClient 的构造函数this(new Builder());
看到这里是否是很熟悉,没错就是咱们熟悉的建造者模式,那么咱们也能够经过以下链式调用进行一些参数配置,
OkHttpClient build = new OkHttpClient.Builder().
addInterceptor().
addNetworkInterceptor().
writeTimeout().
build();
复制代码
下面咱们接着看 this(new Builder());
看到 Builder 中的注释咱们得知,就是配置一大堆 OKHttp 属性,你们想想若是这里实现的是 new OKHttpClient("","","",n+ ...)
有不少参数这样是否是很繁琐,其实你们若是在项目中碰见不少参数须要配置的话,也能够进行 Builder 模式
进行设计。
构建一个 Request 请求
Request request = new Request.Builder()
.url(url)
.build();
复制代码
能够看到上面构建请求也是一个建造者模式,咱们看看 Request 类中作了什么
public final class Request {
...//部分代码省略
public static class Builder {
//URL 管理
@Nullable HttpUrl url;
//请求类型,支持(GET,HEAD,POST,DELETE,PUT,PATCH)
String method;
//Http消息的头字段
Headers.Builder headers;
//传给服务器的 body 数据
@Nullable RequestBody body;
Map<Class<?>, Object> tags = Collections.emptyMap();
public Builder() {
this.method = "GET"; //默认 GET
this.headers = new Headers.Builder();
}
Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tags = request.tags.isEmpty()
? Collections.emptyMap()
: new LinkedHashMap<>(request.tags);
this.headers = request.headers.newBuilder();
}
public Builder url(HttpUrl url) {
if (url == null) throw new NullPointerException("url == null");
this.url = url;
return this;
}
....//省略部分代码
}
复制代码
分析完 Request 咱们知道,就是封装请求信息。
建立 RealCall
okHttpClient.newCall(request)
复制代码
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
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.transmitter = new Transmitter(client, call);
return call;
}
复制代码
能够看到 newCall 根据 Request 会建立一个 Call 它的实现类也就是 RealCall。
同步执行
okHttpClient.newCall(request).execute();
复制代码
@Override public Response execute() throws IOException {
synchronized (this) {
//1. 是否已经执行过了
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();
transmitter.callStart();
try {
//2. 调用 OKHttpClient 的调度器分发给同步执行
client.dispatcher().executed(this);
//3. 调用拦截器
return getResponseWithInterceptorChain();
} finally {
//不论是否执行成功都关闭当前请求任务
client.dispatcher().finished(this);
}
}
复制代码
经过 execute
函数内部实现,咱们知道
咱们看注释 2 代码实现
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
复制代码
将当前执行请求的 call 添加进行同步队列中,这里出现了一个 runningSyncCalls
容器,咱们具体看下是什么
/** 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<>();
复制代码
根据上面注释可知:
readyAsyncCalls:准备须要执行的异步任务
runningAsyncCalls:正在执行的异步任务
runningSyncCalls:正在执行的同步任务
注释 3 的代码咱们待会跟讲解异步源码一块儿看,由于它们最后都会调用 getResponseWithInterceptorChain
函数
异步处理
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
复制代码
由于 Call 的实现类是 RealCall ,因此咱们直接看 RealCall 的 enqueue
@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));
}
复制代码
很明显这里跟同步执行的其实都大同小异,也都只容许执行一次,最后调用分发器进行 enqueue
enqueue
void enqueue(AsyncCall call) {
synchronized (this) {
//1.
readyAsyncCalls.add(call);
//2.
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
//3.
promoteAndExecute();
}
复制代码
根据注释首先将要执行的异步任务加入准备执行的异步队列中,而后判断是不是 WebSocket 最后调用 promoteAndExecute
是要准备执行了,咱们看下它的实现:
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
//1.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
//2.
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
//3.
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
//4.
i.remove();
asyncCall.callsPerHost().incrementAndGet();
//5.
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
//6.
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
复制代码
根据上面代码标注的注释,咱们来分析下具体意思
咱们来看下 asyncCall.executeOn(executorService());
这里的 executorService()
是什么了?
//返回一个执行线程池的对象
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;
}
复制代码
根据上面代码能够知道,返回的是一个执行线程池的对象,看下内部参数表明的意思,该方法是一个同步方法,ThreadPoolExecutor() 的核心线程数量为 0 ,若是空闲的话是 60 s 的存活期,第二个参数传入了 Integer 的最大值,即线程池所能容纳的最大线程数为 Integer.MAX_VALUE ,虽然这里设置了很大的值,可是实际状况下并不是会达到最大值,由于上面 enqueue() 方法中有作了判断。
既然执行了子线程那么确定会走 run 函数,咱们找一下
final class AsyncCall extends NamedRunnable {
....//经过源码查看 AsyncCall 内部没有 run 函数,那么咱们找它的父类
}
复制代码
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
@Override protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();
try {
//1.
Response response = getResponseWithInterceptorChain();
signalledCallback = true;
//2.
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 {
//3.
client.dispatcher().finished(this);
}
}
复制代码
经过上面代码注释分析得知:
getResponseWithInterceptorChain();
函数咱们能够拿到服务器返回的响应数据到了这里 同步执行
、异步执行
最后都调用getResponseWithInterceptorChain();
函数,下面咱们来看下它的具体实现
getResponseWithInterceptorChain();
Response getResponseWithInterceptorChain() throws IOException {
/** * 分类:一、应用拦截器 二、网络拦截器 */
List<Interceptor> interceptors = new ArrayList();
//用户添加的全局拦截器
interceptors.addAll(this.client.interceptors());
//错误、重定向拦截器
interceptors.add(new RetryAndFollowUpInterceptor(this.client));
//桥接拦截器,桥接应用层与网络层,添加必要的头
interceptors.add(new BridgeInterceptor(this.client.cookieJar()));
//缓存处理,Last-Modified、ETag、DiskLruCache等
interceptors.add(new CacheInterceptor(this.client.internalCache()));
//链接拦截器
interceptors.add(new ConnectInterceptor(this.client));
if (!this.forWebSocket) {
//经过okHttpClient.Builder#addNetworkInterceptor()传进来的拦截器只对非网页的请求生效
interceptors.addAll(this.client.networkInterceptors());
}
//真正访问服务器的拦截器
interceptors.add(new CallServerInterceptor(this.forWebSocket));
//一个包裹这request的chain
Chain chain = new RealInterceptorChain(interceptors, this.transmitter, (Exchange)null, 0, this.originalRequest, this, this.client.connectTimeoutMillis(), this.client.readTimeoutMillis(), this.client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
Response var5;
try {
//开始执行拦截器
Response response = chain.proceed(this.originalRequest);
if (this.transmitter.isCanceled()) {
Util.closeQuietly(response);
throw new IOException("Canceled");
}
var5 = response;
} catch (IOException var9) {
calledNoMoreExchanges = true;
throw this.transmitter.noMoreExchanges(var9);
} finally {
if (!calledNoMoreExchanges) {
this.transmitter.noMoreExchanges((IOException)null);
}
}
return var5;
}
复制代码
上面代码发现 new了一个 ArrayList ,而后就是不断的 add,而后 new 了 RealInterceptorChain 对象,最后调用了chain.proceed() 方法。先看下RealInterceptorChain 的构造函数。
public RealInterceptorChain(List<Interceptor> interceptors, Transmitter transmitter, @Nullable Exchange exchange, int index, Request request, Call call, int connectTimeout, int readTimeout, int writeTimeout) {
this.interceptors = interceptors;
this.transmitter = transmitter;
this.exchange = exchange;
this.index = index;
this.request = request;
this.call = call;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
this.writeTimeout = writeTimeout;
}
复制代码
构造函数里面其实没有作些什么,就是一些赋值动做。最后看下 chain.proceed() 内部实现
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange) throws IOException {
//若是当前 index 大于总的拦截器数量就抛异常
if (index >= interceptors.size()) throw new AssertionError();
//每调用一次就自增长 1
calls++;
// 若是咱们已经有了一个流,请确认传入的请求将复用它
if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// 若是咱们已经有了一个流,请确认这是对chain.proceed()的惟一调用.
if (this.exchange != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// 内部又建立一个 RealInterceptorChain 对下一个拦截器处理
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
//拿到当前的拦截器
Interceptor interceptor = interceptors.get(index);
//执行下一个拦截器,返回 response
Response response = interceptor.intercept(next);
....
return response;
}
复制代码
根据上面代码看到在 proceed 方面里面又 new 了一个 RealInterceptorChain 类的 next 对象,这个 next 对象和 chain 最大的区别就是 index 属性值不一样 chain 是 0. 而 next 是1,而后取 interceptors 下标为 1 的对象的 interceptor。由从上文可知,若是没有开发者自定义的Interceptor 时,首先调用的 RetryAndFollowUpInterceptor,若是有开发者本身定义的interceptor 则调用开发者interceptor。
这里须要说明一下,由于拦截器在 OKHttp 设计中最为重要或是最值得学习之一,咱们须要单独写一篇介绍拦截器,因此这里咱们着重介绍执行流程
后面的流程是在每个 interceptor 的 intercept 方法里面都会调用 chain.proceed()
从而调用下一个 interceptor
的 intercept(next)
方法,这样就能够实现递归遍历getResponseWithInterceptorChain
里面的 interceptors
,不知道这种调用方式你们有没有熟悉感,是否是以为跟 责任链模式
很相像。没错,它其实就是设计模式当中的责任链模式。每条链处理的任务都是独立的,也实现了解耦功能,它的强大彻底在这里体现出来了。缩减后的责任链模式拦截器代码以下:
//RetryAndFollowUpInterceptor.java
public Response intercept(Chain chain) throws IOException {
//忽略部分代码
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
//忽略部分代码
}
复制代码
//BridgeInterceptor.java
public Response intercept(Chain chain) throws IOException {
//忽略部分代码
Response networkResponse = chain.proceed(requestBuilder.build());
//忽略部分代码
}
复制代码
//CacheInterceptor.java
public Response intercept(Chain chain) throws IOException {
//忽略部分代码
networkResponse = chain.proceed(networkRequest);
//忽略部分代码
}
复制代码
//ConnectInterceptor.java
public Response intercept(Chain chain) throws IOException {
//忽略部分代码
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
复制代码
读过源码咱们知道 getResponseWithInterceptorChain
里面interceptors
的最后一个 拦截器 是 CallServerInterceptor.java
,最后一个 Interceptor (即CallServerInterceptor) 里面是直接返回了response 而不是进行继续递归,具体里面是经过OKIO
实现的,具体代码,等后面再详细说明,CallServerInterceptor
返回response
后, 返回给上一个 interceptor ,通常是开发者本身定义的 networkInterceptor ,而后开发者本身的 networkInterceptor 把他的 response 返回给前一个 interceptor ,依次类推返回给第一个 interceptor,这时候又回到了 realCall 里面的 execute() 里面了,代码以下:
@Override protected void execute() {
...
try {
Response response = getResponseWithInterceptorChain();
signalledCallback = true;
//将 response 返回给调用层
responseCallback.onResponse(RealCall.this, response);
} catch (IOException e) {
.... //省略代码
}
复制代码
最后经过 responseCallback.onResponse(RealCall.this, response);
回调返回给了调用层,至此,请求 -> 响应流程咱们分析完了,最后咱们来总结下
同步请求流程:
Dispatcher.executed()
中的 runningSyncCalls
将 Realcall 添加到同步执行队列中getResponseWithInterceptorChain()
内部的责任链调用对 Request 层层处理包装,最后在 CallServerInterceptor
拦截器中发起请求和得到 ResponseDispatcher.finished()
,把 call 实例从队列中移除,并执行下一次任务。异步请求流程:
建立 OKHttpClient 实例,配置属性
构建 Request ,配置属性
构建 Realcall,调用 enqueue
生成一个AsyncCall(responseCallback)
实例(实现了Runnable)
AsyncCall
实例放入了Dispatcher.enqueue()
中,并判断 maxRequests
(最大请求数 64)maxRequestsPerHost
(最大host请求数 5)是否知足条件,若是知足就把AsyncCall
添加到runningAsyncCalls
中,并放入线程池中执行;若是条件不知足,就添加到等待就绪的异步队列,当那些知足的条件的执行时 ,在Dispatcher.finifshed(this)
中的promoteCalls();
方法中 对等待就绪的异步队列进行遍历,生成对应的AsyncCall
实例,并添加到runningAsyncCalls
中,最后放入到线程池中执行,一直到全部请求都结束。