0.前言
欢迎阅读笔者文章,本文是OkHttp源码解析系列文章的第二篇,如果没看过上一篇文章的,请转OkHttp源码分析之(一)请求流程,有什么不足的地方,记得提出来哦,您的支持,是对我写作的最大支持。
皮一下:笔者在android技术上遇到瓶颈了,但是通过这几个星期对框架源码的学习,终于得到突破了,强烈推荐在技术上遇到瓶颈的童鞋尝试阅读下流行框架的源码。这玩意,还很容易上瘾>_<
1.Dispatcher
在上一篇文章中,已经对Dispatcher里面的部分方法进行了解析,在这里,我将站在总体的角度上对其进行一个总结。
(1)Dispatcher做了什么?
先来看看Dispatcher里面定义的参数:
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 readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque runningSyncCalls = new ArrayDeque<>();
}
可以看到,Dispatcher里面维护了三个请求队列:同步请求队列、异步请求就绪队列、异步请求正在执行队列,没错,Dispatcher就干维护这三个请求队列、处理所有请求这两件事。
(2)Dispatcher为什么要维护这三个队列?
这个问题好解释,还是回到上一节讲到的网略请求部分,先看看同步请求:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);//代码1,入队
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);//代码2,出队
}
}
/**Dispatcher中**/
//代码1
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
//代码2
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call);
}
private void finished(Deque 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 executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
//遍历就绪异步请求队列(有异步请求时才会执行)
for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();//从就绪队列中取出请求
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.//如果正在运行的异步请求没有超限
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.//并且Host请求没有大于最大数
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;
}
代码在上一篇文章中已经解析过,这里就不解析了,主要看代码1和代码2,同步请求主要就干了两件事:入队和出队,这里重点还是promoteAndExecute()方法,当执行同步请求是,在上一步重正在执行的同步请求队列中移除掉请求之后,这里仅仅是重新计算正在执行的队列数(等于runningAsyncCalls.size() + runningSyncCalls.size());如果是异步请求,则会执行注释中的代码,其中重要的一点就是当runningAsyncCalls.size()超额时仅会把请求放到就绪的异步请求队列(readyAsyncCalls)中(breake了),而这里,则是当runningAsyncCalls小于maxRequests时(有空位了),将异步请求从就绪队列readyAsyncCalls中取出来放到runningAsyncCalls之中。
因此,Dispatcher才需要维护这三个队列。
关于Dispatcher就介绍到这里,接下来介绍下OkHttp另一核心内容:拦截器
2.拦截器
啥?什么拦截器?我怎么没听说过呀?
别紧张,先来看看拦截器的是什么。
拦截器是什么?官网的解释是:
Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.
拦截器是OkHttp中提供的一种强大机制,可以实现网略监听、重写和请求、请求失败重试等功能。
官网
顺便附上官网提供的图:
由官方解释可见拦截器是不管同步还是异步请求都会执行的。
关于拦截器的分析,由于代码实在是太多了,由于篇幅关系,无法进行详细解释,但是流程还是要有的,做了什么也是要交代清楚的,于是,在这里,我就简单讲讲这厮做了什么。
(1)流程
首先还是从RealCall的代码中入手(execute中和Async里面的execute,同步异步都会执行),这里举个栗子就从同步请求入手吧:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
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);
}
}
重点就是getResponseWithInterceptorChain()方法,来看看它做了什么:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
//包含所有拦截器的List
List interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());//将用户定义的拦截器添加进来,即上图中的Application拦截器
//添加OkHttp内部的五大拦截器
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);
}
可以看到,先是将用户自定义的拦截器添加进拦截器列表中,后又将OkHttp内部的五个拦截器分别添加进去,这五个拦截器分别:
- retryAndFollowUpInterceptor:重试和失败重定向拦截器
- BridgeInterceptor:桥接适配器拦截器
- CacheInterceptor:缓存拦截器
这两个拦截器主要负责补充请求头等信息和处理缓存。 - ConnectInterceptor:连接拦截器。负责连接。
- CallServerInterceptor:服务响应拦截器。负责将Http请求写入到网略中,并从网略IO流中读取数据返回给客户端。
接下来是重点,这里为什么说是拦截器链呢?请往下看RealInterceptorChain:
private final List interceptors;
private final StreamAllocation streamAllocation;
private final HttpCodec httpCodec;
private final RealConnection connection;
private final int index;
private final Request request;
private final Call call;
private final EventListener eventListener;
private final int connectTimeout;
private final int readTimeout;
private final int writeTimeout;
private int calls;
public RealInterceptorChain(List interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
this.call = call;
this.eventListener = eventListener;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
this.writeTimeout = writeTimeout;
}
构造方法主要初始化了一些数据,这里不多讲,关键在于它的proceed方法:
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
//重点1:index + 1,即下一个拦截器
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);//将下一个拦截器传递进去
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
这里重要的一点就是indext+1,表示只能访问下一个拦截器,这样就把所有的拦截器连接起来成为一条链了。
总结起来,getResponseWithInterceptorChain做了两件事:
- 将五大拦截器添加进List中
- 创建拦截器链RealInterceptorChain,并执行proceed方法
而具体的拦截器里面执行的其实也是下一个拦截器的proceed方法,这里以RetryAndFollowUpInterceptor未来,看看他里面的intercept方法:
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
response = realChain.proceed(request, streamAllocation, null, null);//重点,代码1
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.getFirstConnectException();
}
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;
try {
followUp = followUpRequest(response, streamAllocation.route());
} catch (IOException e) {
streamAllocation.release();
throw e;
}
if (followUp == null) {
streamAllocation.release();
return response;
}
closeQuietly(response.body());
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
重点看代码1,由前面分析可知,这里执行的是下一个拦截器的intercept,而下一个拦截器的intercept又会执行realChain.proceed,由此形成连接,将所有拦截器连接起来。
嘛,流程图还是要有的:
原创文章,欢迎转载,转载请附上原文地址https://www.jianshu.com/p/0f955f38ada1