OkHttp的分发器与拦截器详解
上文我们有提到过的okHttpClient中比较重要的几个属性
Dispatcher dispatcher; //分发器
final List<Interceptor> interceptors = new ArrayList<>(); //application级拦截器
final List<Interceptor> networkInterceptors = new ArrayList<>(); //低一级的拦截器
分发器(Dispatcher)
我们先来看一下call.execute()这个方法中到底发生了什么可以得到一个response对象。
//RealCall.java
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
我们可以看到try里面的第一句就是client.dispatcher().executed(this)。client.dispatcher()就是获取该okHttpClient对象的一个对应的分发器,儿这个分发器其实在okHttpClient的构造方法中已经初始化好了,我们点进去看一下这个executed(RealCall call)。
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
//runningSyncCalls 是一个Deque<RealCall>对象
runningSyncCalls是一个双向队列,也就相当于把这个要进行网络访问的call加入到一个双队列中。
接下来的getResponseWithInterceptorChain()的方法就会在分析拦截器的时候再进行分析,怎么就可以得到一个Response对象。
下面我们再来看看call.enqueue()发生了什么
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
我们跟进去看一下diapatcher.enqueue方法
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
因为是异步请求,所以在同一时间发出的最大请求量,与同一主机的最大访问量都有一个最大值。if的判断条件中maxRequests是最大请求量,默认是60,maxRequestsPerHost是同一主机访问最大量,默认是5。如果满足这个条件,我们就像要执行任务的双向队列中加入这个call对象,然后调用executorService().execute(call)方法。如果超过了呢,就往readyAsyncCalls中添加这个对象,readyAsyncCalls也是一个双向队列,不过它与runningAsyncCalls的区别是它里面包含的都是准备执行的call,不是正在执行的call,当我们一个call执行完的时候,会再去判断readyAsyncCalls是否有call,有就取一个最早加入的call加入runningAsyncCalls,并且调用executorService().execute(call)方法,那么executorSercvie()方法
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中execute(call)方法发生了什么
@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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
try里面的第一句就是调用这个方法getResponseWithInterceptorChain(),然后后面就是比较常规的回调onFailure()和onResponse()方法,看来很关键的一个问题就是这个getResponseWithInterceptorChain()发生了什么,我们点进去看一下。
拦截器
private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
}
看起来比较简单,首先加入在构造okHttpClient可以定义的interceptors,前面也说了这个application级别的拦截器,它最先加入,接着是系统自带的一些拦截器。然后加入自定义的networkInterceptors拦截器,最后加入一个CallServerInterceptor拦截器。再构建一个Interceptor.Chain对象,这个对象是一个拦截器链,就是相当于我们刚传进去的拦截器的一个链表一样,注意这里传进去一个参数index 为0,然后调用chain.proceed方法,我们跟进去
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpStream, connection);
}
再跟这个proceed方法
public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
Connection 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.httpStream != null && !sameConnection(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.httpStream != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
//注意这里,又去构建一个index = index + 1 的拦截器链
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpStream, connection, index + 1, request);
//获取拦截器,下标为0开始
Interceptor interceptor = interceptors.get(index);
// 这个方法很重要,跟进去看看
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpStream != 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");
}
return response;
}
我们再看看interceptor.intercept(next)方法。
这个方法intercept 各个拦截器有各个的实现方法,我们自定义拦截器也是要去实现这个方法,那我们看一下最先加进去的retryAndFollowUpInterceptor是怎么实现的
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()));
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
boolean releaseConnection = true;
try {
response = ((RealInterceptorChain) chain).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(), true, request)) throw e.getLastConnectException();
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
if (!recover(e, false, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
内容比较多,其实可以这么去看
处理request
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
处理response
这样就清晰很多了,一个很标准的递归,就是相当于假设我有三个拦截器,一号拦截器最先加进去,一号先处理request,处理完做为参数传给二号,二号也处理request再传给三号,三号发送request得到最开始的response返回给2号,2号处理一下response再返回给1号,然后最后就成了我们得到的response对象,那么按照这么说,最下面的拦截器应该就不会调用proceed方法了把,的确如此,最下面的拦截器是CallServerInterceptor,我们点进去看一下它的intercept方法。
@Override public Response intercept(Chain chain) throws IOException {
HttpStream httpStream = ((RealInterceptorChain) chain).httpStream();
StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();
Request request = chain.request();
long sentRequestMillis = System.currentTimeMillis();
httpStream.writeRequestHeaders(request);
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
Sink requestBodyOut = httpStream.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
httpStream.finishRequest();
Response response = httpStream.readResponseHeaders()
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
if (!forWebSocket || response.code() != 101) {
response = response.newBuilder()
.body(httpStream.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
int code = response.code();
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
可以看到的确没有再调用那个方法,它其实已经在和服务端进行数据交流了,得到原始的response。