Android - 剖析OKHttp(4)- 拦截器RetryA
2020-11-06 本文已影响0人
杨0612
源码分析基于 3.14.4
RetryAndFollowUpInterceptor作用
从名字就能看出,他是负责失败重试以及重定向。
//RetryAndFollowUpInterceptor类中
@Override public Response intercept(Chain chain) throws IOException {
......
while (true) {
......
try {
response = realChain.proceed(request, transmitter, null);//1
success = true;
} catch (RouteException e) {//2
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
continue;
} catch (IOException e) {//3
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, transmitter, requestSendStarted, request)) throw e;
continue;
}
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Exchange exchange = Internal.instance.exchange(response);
Route route = exchange != null ? exchange.connection().route() : null;
Request followUp = followUpRequest(response, route);//4
```
if (followUp == null) {//6
if (exchange != null && exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
return response;
}
......
RequestBody followUpBody = followUp.body();
if (followUpBody != null && followUpBody.isOneShot()) {
return response;
}
......
if (++followUpCount > MAX_FOLLOW_UPS) {///5
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
request = followUp;
priorResponse = response;
}
}
- 核心功能在intercept函数中;
- 注释1:一般的拦截器都会有预处理,然后再丢给下一个拦截器处理,但是它没有,它直接丢给下一个拦截器了;
- 注释2:如果发生RouteException(路由异常),通常是因为跟服务器没有建立连接,例如域名对应多个ip,其中一个ip连接失败,则重试另外的ip;
- 注释3:如果发IOException,连接已经建立,在读写的过程中服务器发生了异常,例如down机了,则重试;;
- 注释4:当不是发生 RouteException或IOException异常,则通过followUpRequest返回结果判断是否需要重试;
- 注释5:限制重试次数为20次;
- 注释6:当followUp为null,则表示返回结果正常,即返回结果;
//RetryAndFollowUpInterceptor类中
private Request followUpRequest(Response userResponse, @Nullable Route route) throws IOException {
......
switch (responseCode) {
case HTTP_PROXY_AUTH://1
......
return client.proxyAuthenticator().authenticate(route, userResponse);
case HTTP_UNAUTHORIZED://2
return client.authenticator().authenticate(route, userResponse);
......
case HTTP_MOVED_PERM://3
case HTTP_MOVED_TEMP://4
if (!client.followRedirects()) return null;
String location = userResponse.header("Location");
if (location == null) return null;
HttpUrl url = userResponse.request().url().resolve(location);
......
return requestBuilder.url(url).build();
case HTTP_CLIENT_TIMEOUT://5
......
return userResponse.request();
......
default:
return null;
}
}
- 主要是根据服务器返回码判断是否需要重试;
- 注释1、2:当返回码为401或者407,表示鉴权有问题,会回调client.authenticator().authenticate(route, userResponse)或者client.proxyAuthenticator().authenticate(route, userResponse),提供一次获取新验证码写入Request头的机会,接着开始重试;
- 注释3、4:当返回码为301或者302,表示需要重定向,则用新的url构建Request开始重试;301或者302的响应头,带有Location,就是重定向的地址,例如Location:www.baidu.com
- 注释5:返回码408,如果允许重试(client.retryOnConnectionFailure()为true),并且服务器没有返回Retry-After 延迟时间,则可以立即开始重试;
- 301表示永久重定向,302表示临时重定向,408表示请求超时,401表示授权失败,407表示代理授权失败;
总结:
- RetryAndFollowUpInterceptor负责失败重试以及重定向;
- RetryAndFollowUpInterceptor.intercept函数,内部是while循环处理,当正常返回结果则退出循环,否则重试;
- 当发生RouteException或IOException或根据返回码,考虑需要重试;
- 301表示永久重定向,302表示临时重定向,408表示请求超时,401表示授权失败,407表示代理授权失败;
- 重试次数限制在20次;
以上分析有不对的地方,请指出,互相学习,谢谢哦!