关于OkHttp的使用可以看这篇文章https://blog.csdn.net/Icarus_/article/details/103702167
OkHttp的源码地址https://github.com/square/okhttp,我这里看的是3.10.0版本
创建方式有2种,一种是通过建造者模式建造出来,一种是直接new出来,直接new一个OkHttpClient对象内部也是通过建造者模式完成初始化。
public OkHttpClient() {
this(new Builder());
}
看一下OkHttpClient有哪些参数
OkHttpClient(Builder builder) {
//分发器dispatcher对象,记录请求执行情况,内部维护一个线程池执行异步请求
this.dispatcher = builder.dispatcher;
//配置代理类型(直连、HTTP代理、Socket代理),以及socket地址
this.proxy = builder.proxy;
//协议及和,默认协议包含HTTP2和HTTP1.1
this.protocols = builder.protocols;
//连接规范,指定socket连接的配置
this.connectionSpecs = builder.connectionSpecs;
//应用拦截器集合
this.interceptors = Util.immutableList(builder.interceptors);
//网络拦截器集合
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
//事件监听工厂
this.eventListenerFactory = builder.eventListenerFactory;
//代理选择器
this.proxySelector = builder.proxySelector;
//为HTTP的cookies提供策略和持久化
this.cookieJar = builder.cookieJar;
//磁盘缓存,内部使用DiskLruCache
this.cache = builder.cache;
//OkHttp内部缓存
this.internalCache = builder.internalCache;
//socket工厂类
this.socketFactory = builder.socketFactory;
//https相关成员变量初始化
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 = systemDefaultTrustManager();
this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
......
//连接池
this.connectionPool = builder.connectionPool;
//DNS
this.dns = builder.dns;
//是否跟随SSL重定向
this.followSslRedirects = builder.followSslRedirects;
//是否跟随重定向
this.followRedirects = builder.followRedirects;
//连接失败是否重试
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
//连接是否超时
this.connectTimeout = builder.connectTimeout;
//连接超时时间
this.readTimeout = builder.readTimeout;
//读超时时间
this.writeTimeout = builder.writeTimeout;
//ping间隔时长
this.pingInterval = builder.pingInterval;
......
}
创建好OkHttpClient后,完成了许多成员变量的初始化,接下来是使用OkHttpClient对象创建一个Call对象。Call对象可以当作对请求request的再次封装,表示该请求已经准备好可以随时发送。此外,一个Call可以取消,但是不允许执行2次。Call的创建如下
okHttpClient.newCall(request);
//返回一个RealCall对象
@Override public Call newCall(Request request) {
//第一个参数,okHttpClient对象
//第二个参数,将要发送的请求
//第三个参数,是否使用websocket
return RealCall.newRealCall(this, request, false /* for web socket */);
}
看一下dispatcher的源码中很重要的3个变量
//线程池
private @Nullable ExecutorService executorService;
//正在执行的异步请求,包含了已经取消但没有执行完的异步请求
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//就绪状态的请求队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
为什么异步请求需要2个队列呢,可以将dispatcher理解为生产者消费者模型,所以需要2个队列来存放正在执行的和等待的异步请求
通过newCall方法返回的实际上是RealCall对象,如果是同步请求,调用RealCall对象的execute方法执行请求
okHttpClient.newCall(request).execute();
@Override public Response execute() throws IOException {
synchronized (this) {
//如果已经执行过了,抛出异常
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
//获取OkHttpClient对象中的dispatcher对象
//调用dispatcher对象的executed方法标记请求已经发送
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);
}
}
看一下dispatcher().executed,每次有新的请求到来时候直接加入到运行队列中
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
异步请求调用的是RealCall对象的enqueue方法,传入一个回调接口Callback对象,内部调用dispatcher对象的enqueue方法传入,传入一个AsyncCall对象,AsyncCall作为一个Runnable
okHttpClient.newCall(request).enqueue();
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
//调用dispatcher对象的enqueue方法传入,传入一个AsyncCall对象
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
而在dispatcher对象的enqueue方法中,会使用线程池执行AsyncCall这个Runnable
synchronized void enqueue(AsyncCall call) {
//检查请求是否超过最大请求数,和一个HOST对应的最大请求数
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//添加到正在请求队列中,标记异步请求正在执行
runningAsyncCalls.add(call);
//使用线程池执行AsyncCall
executorService().execute(call);
} else {
//将请求加入到就绪异步等待队列,缓存等待
readyAsyncCalls.add(call);
}
}
这个ExecutorService的创建和一般的ThreadPoolExecutor的创建都是类似的,但是注意下前三个参数
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;
}
接下来就要看一下AsyncCall是怎么执行的了,AsyncCall是Dispatcher的内部类,继承自NamedRunnable,NamedRunnable会保存自己执行线程的线程名,在其run方法中调用了一个抽象方法execute,子类实现execute方法完成任务。看一下这个Runnable
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();
}
AsyncCall中也是调用核心代码getResponseWithInterceptorChain获取网络响应结果,然后回调Callback。AsyncCall的实现如下
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
......
@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) {
.......
} finally {
//请求完之后finish
client.dispatcher().finished(this);
}
}
}
看一下这个dispatcher().finished方法,
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();
}
}
拦截器是okHttp中一个强大的机制,可以实现网络监听,请求,响应重写,失败重试等。
同步和异步请求都调用了getResponseWithInterceptorChain()方法,方法中创建了一个拦截器集合,先后顺序依次是:应用拦截器,retryAndFollowUpInterceptor,BridgeInterceptor,CacheInterceptor,ConnectInterceptor,网络拦截器,CallServerInterceptor。
Response getResponseWithInterceptorChain() throws IOException {
//拦截器集合
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));
//创建RealInterceptorChain
//第一个参数是拦截器集合
//第二个参数是指向拦截器的下标,0表示拦截器集合中的第一个
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
//启动拦截器链
return chain.proceed(originalRequest);
}
拦截器是OkHttp中最精巧的设计,遵循单一职责原则,每个拦截器都只针对网络请求或者网络响应做一项处理,拦截器之间使用RealInterceptorChain连接,整个拦截器构成了处理网络请求和响应的流水线。原始请求作为材料,经过拦截器的层层加工,然后发送出去,返回的网络请求也经过拦截器层层处理,最终返回给应用层。
在getResponseWithInterceptorChain方法中,创建了拦截器集合,初始化第一个RealInterceptorChain对象,将他指向拦截器集合的第一个拦截器,就是把index赋值为0,然后调用RealInterceptorChain对象的proceed方法启动拦截器链
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
......
//创建一个新的RealInterceptorChain
//第五个参数index + 1表示下一个拦截器的下标
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//通过下标获取当前要执行的拦截器
Interceptor interceptor = interceptors.get(index);
//当前拦截器处理完后,要执行下一个拦截器,则调用传入的next的proceed方法
Response response = interceptor.intercept(next);
......
return response;
}
在proceed方法中,通过下标index获取到当前应该执行的拦截器,然后调用该拦截器的intercept方法,并传入一个新建的RealInterceptorChain对象的next,next指向下一个拦截器。所以一般情况下,如果一个拦截器处理完成之后,想要执行下一个拦截器,则可以在拦截器的intercept方法中调用next.proceed方法,拦截器集合中的拦截器就能一个个按顺序执行
1.创建一系列拦截器,放入拦截器集合中
2.创建一个拦截器链RealInterceptorChain,执行拦截器链的proceed方法
3.发起请求前对request进行处理
4.调用下一个拦截器获取response
5.对response进行处理,返回给上一个拦截器
拦截器链启动后,拦截器依次执行,首先执行的是应用拦截器,如果没有设置应用拦截器,则执行RetryAndFollowUpInterceptor,他处理错误恢复和重定向。如果在执行请求过程中出现异常,他会判断如果满足重试条件就发送重试失败的请求,如果网络响应中包含重定向信息,他就会创建重定向请求重新发送,他的intercept方法如下
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
......
//用来建立执行http请求所需要的网络组件,他在这个拦截器中并没有被使用到,
//会在ConnectInterceptor使用,主要用于获取连接服务端的connection,和用于服务端之间数据传输的输入输出流
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
......
while (true) {
......
Response response;
boolean releaseConnection = true;
try {
//执行下一个拦截器获取响应
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
//如果满足条件则重试
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
releaseConnection = false;
//如果满足条件则重试
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
//获取重定向请求
Request followUp = followUpRequest(response, streamAllocation.route());
//如果重定向请求为空
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
//不需要重定向,直接返回
return response;
}
//对重试次数进行判断
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
......
//检查是否可以执行重定向,不满足会抛出异常
......
//将请求重新赋值为重定向的请求,继续while循环,再次发送
request = followUp;
priorResponse = response;
}
}
总结一下:
1.创建StreamAllocation对象
2.调用RealInterceptorChain.proceed进行网络请求
3.根据异常结果和响应结果判断是否需要重新请求
4.调用下一个拦截器,对response进行处理,返回给上一个拦截器
在RetryAndFollowUpInterceptor的intercept方法中,调用RealInterceptChain对象的proceed方法,由于RealInterceptChain的下标已经指向了RetryAndFollowUpInterceptor后面的拦截器BridgeInterceptor,所以接下来执行BridgeInterceptor的intercept方法。BridgeInterceptor是桥接器,桥接应用层和网络层的代码,这里会将用户传递过来的请求进行加工,使之成为真正的网络请求,也会对网络响应做响应的处理。如果用户在请求头中没有配置Accenting-Encoding字段,则会将其设置成gzip,服务器会返回gzip压缩的网络响应数据,相应地客户端也会对网络响应做压缩处理,他的intercept方法如下
@Override public Response intercept(Chain chain) throws IOException {
//获取用户请求
Request userRequest = chain.request();
//真正发送网络请求发构建者
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
//设置一些请求头
//Content-Type,Content-Length,Transfer-Encoding,Host,Connection,
//Accept-Encoding,Cookie,User-Agent
......
//执行下一个拦截器,发送请求,获取网络响应
Response networkResponse = chain.proceed(requestBuilder.build());
//将网络请求,将服务器返回的response转化为用户可以使用的response
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
//在请求头中没有配置Accenting-Encoding字段,则会将其设置成gzip,
//服务器会返回gzip压缩的网络响应数据,相应地客户端也会对网络响应做压缩处理
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
//GzipSource用于解压缩
GzipSource responseBody = new GzipSource(networkResponse.body().source());
//返回应用层之前去掉不需要的头
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
总结一下:
1.将用户构建好的request请求转化为能够进行网络访问的请求
2.将符合网络请求的request进行网络请求
3.将服务器返回的response转化为用户可以使用的response
BridgeInterceptor之后的下一个拦截器是CacheInterceptor,他用于缓存的查找与保存,网络请求到来后,先查找有没有这个请求对应的缓存,如果有则直接返回缓存,没有则调用后面的拦截器发送网络请求,并将获取到的响应缓存起来。他的Cache是通过DiskLruCache算法实现的
CacheInterceptor的intercept方法实现如下
@Override public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
//检查缓存策略
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
//如果还是要发送网络请求则networkRequest和cacheResponse不为null
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
......
// If we don't need the network, we're done.
//如果缓存可用,则不发送网络请求,直接返回
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
//如果缓存不可用,则执行下一个拦截器,获取网络响应
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
//包装网络响应
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
//如果用户配置了缓存
if (cache != null) {
//是否满足缓存条件
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);
//将响应写入缓存并返回
return cacheWritingResponse(cacheRequest, response);
}
//判断是否是无效的缓存方法
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
//如果是,要在缓存池中删除request
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
ConnectInterceptor主要是给网络请求提供一个连接,然后执行下一个拦截器,看他的intercept方法
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//获取一个HTTP编码解码器,为Http1Codec或者Http2Codec
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
//RealConnection是做实际的网络传输的,获取一个连接
RealConnection connection = streamAllocation.connection();
//执行下一个拦截器
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
总结:
1.ConnectInterceptor获取Interceptor传过来的streamAllocation
2.将刚才创建的用于网络IO的RealConnection对象,以及和服务器交互的最为关键的HttpCodec等对象传递给后面的拦截器
不管是http1.0还是2.0的keepAlive还是多路复用机制都需要引入连接池的概念,来维护整个okHttp的网络连接,okhttp会将客户端和服务端抽象成connection类,RealConnection就是他的实现类,为了管理所有的RealConnection,提供了ConnectionPoll这个类,他就是为了管理这些链接复用的,当他们共享相同的地址,就可以复用链接
如果设置了网络拦截器,那么ConnectInterceptor执行完就会执行网络拦截器,如果没设置,则执行CallServerInterceptor,他是okhttp最后一个拦截器,网络请求最终从这里发出去,他的intercept方法如下
@Override public Response intercept(Chain chain) throws IOException {
......
//使用ConnectInterceptor中获取到的httpCodec发送网络请求
httpCodec.finishRequest();
.......
//构建一个网络响应对象
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
......
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
//写入响应体
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
......
//返回网络响应结果
return response;
}