RetryAndFollowUpInterceptor拦截器分析
源码地址:https://github.com/square/okhttp
前面已经对整体流程以及几个类做了了解,这里就开始对第一个拦截器RetryAndFollowUpInterceptor的分析了。
整体结构
首先通过一张图了解一下这个拦截器的整体结构:
纵观整个类,方法分为了两部分:
- 供外部调用的:cancle相关的 , intercept等 ;
- 内部使用的,主要服务于intercept方法。
那么接下来我们就从两部分对这个类进行分析:
外部调用方法
//进行取消连接的操作
public void cancel() {
canceled = true;
//通过StreamAllocation进行cancle操作
StreamAllocation streamAllocation = this.streamAllocation;
if (streamAllocation != null) streamAllocation.cancel();
}
//获取cancle状态
public boolean isCanceled() {
return canceled;
}
//设置调用堆栈跟踪,在创建StreamAllocation对象的时候作为参数传入
public void setCallStackTrace(Object callStackTrace) {
this.callStackTrace = callStackTrace;
}
//获取StreamAllocation,一个流分配的类,处理连接,数据流,Call请求的关系
public StreamAllocation streamAllocation() {
return streamAllocation;
}
这些方法提供给外部调用,你跟踪一下,会发现其实都是给RealCall调用使用的,而Cancle相关的方法,RealCall 就直接提供给外部使用了。也就是我们取消一个连接的操作。而另外两个方法,则都是提供给内部使用。
这个几个方法中,你会发现,大部分都与这个StreamAllocation类有关。这个类到底是什么?到底做了什么操作?在后面还会用到吗?带着这些疑问,在这一节之后再对这个类进行学习分析。
intercept方法
每个拦截器,最重要的方法就是这个intercept方法了。
而对于RetryAndFollowUpInterceptor这个拦截器都做了些什么?
新建StreamAllocation类,传入okhttpClicent中创建的连接池,传入通过url创建Address类,传入callStackTrace,调用堆栈跟踪。
开启一个while循环;
判读是否用户已经cancle,是-抛出异常,否-继续走下去;
通过RealInterceptorChain调用下一个拦截器(request, streamAllocation),并等待下一个拦截器返回结果。
获取返回结果做两个捕获异常,处理后面步骤抛出的异常, 判断是否继续请求。
判读结果是否符合要求,返回或者进行重定向。
关闭响应结果
判断重定向数目是否超过 MAX_FOLLOW_UPS(20)
判断重新连接是否问相同的连接,否-新建,是-释放。
循环2以后的步骤。
源码:
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//1. 新建StreamAllocation类
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
//2.开启一个while循环
while (true) {
//3.cancle判读
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
boolean releaseConnection = true;
try {
//4.调用下一个拦截器
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.
//5.判断是否继续请求
if (!recover(e.getLastConnectException(), 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, 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();
}
//6.判断是否重定向或者超时重试
Request followUp = followUpRequest(response);
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
// 7. 关闭响应结果
closeQuietly(response.body());
// 8.判断是否重定向数目
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()), callStackTrace);
} 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;
}
}
内部调用方法
提供内部的方法都是服务于interceptor(),有三个:
createAddress,通过url的host,port, dns 以及一系列ohHttpClient的协议连接参数,简历一个Address类。而这个Address对象是提供给StreamAllocation,建立连接使用的。
recover, 连接异常判断,是否继续。(含isRecoverable)
private boolean recover(IOException e, boolean requestSendStarted, Request userRequest) {
streamAllocation.streamFailed(e);
//调用层面,是否禁止了重连重试
// The application layer has forbidden retries.
if (!client.retryOnConnectionFailure()) return false;
//请求的Request本身出错,不能继续再连接
// We can't send the request body again.
if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) return false;
// 是否可以恢复的,主要判断了,是否协议异常,中断异常,SSL的握手异常,SSL通道未许可异常。
// This exception is fatal.
if (!isRecoverable(e, requestSendStarted)) return false;
//没有更多的线路提供了
// No more routes to attempt.
if (!streamAllocation.hasMoreRoutes()) return false;
// For failure recovery, use the same route selector with a new connection.
return true;
}
- followUpRequest, 通过结果的http code码,来判断是否可以重定向,可以正常重定向会返回对应的Request,不然就返回null。
总结:
RetryAndFollowUpInterceptor 的主要做了三件事:
初始化了连接的对象(StreamAllocation,但是比没有真正建立连接,只是初始化了对象)(前置拦截);
通过RealInterceptorChain,再调用下一个拦截器;
收到结果之后,做异常处理,判断是否重连或者重定向,或者返回结果。(后置拦截)
作为第一个拦截器,功能也是相对比较简单的。留下一个疑问就是StreamAllocation 这个类,这个类的学习在以后会专门提出来。
系列:
OKhttp源码学习(一)—— 基本请求流程
OKhttp源码学习(二)—— OkHttpClient
OKhttp源码学习(三)—— Request, RealCall
OKhttp源码学习(五)—— BridgeInterceptor
OKhttp源码学习(六)—— CacheInterceptor拦截器
OKhttp源码学习(七)—— ConnectInterceptor拦截器
OKhttp源码学习(八)——CallServerInterceptor拦截器
OKhttp源码学习(九)—— 任务管理(Dispatcher)