手把手讲解-OkHttp硬核知识点(2)上,成体系化的神级Android进阶笔记,

}
}

上面的代码中,我只保留了关键部分。其中有两个continue,一个return.

当请求到达了这个拦截器,它会进入一个while(true)循环,

  • 当发生了RouteException 异常(这是由于请求尚未发出去,路由异常,连接未成功),就会去判断recover方法的返回值,根据返回值决定要不要continue.
  • 当发生IOException(请求已经发出去,但是和服务器通信失败了)之后,同样去判断recover方法的返回值,根据返回值决定要不要continue.

如果这两个continue都没有执行,就有可能走到最后的return response结束本次请求.
那么 是不是要重试,其判断逻辑就在recover()方法内部:

private boolean recover(IOException e, StreamAllocation streamAllocation,
boolean requestSendStarted, Request userRequest) {
streamAllocation.streamFailed(e);

//todo 1、在配置OkhttpClient是设置了不允许重试(默认允许),则一旦发生请求失败就不再重试
//The application layer has forbidden retries.
if (!client.retryOnConnectionFailure()) return false;

//todo 2、由于requestSendStarted只在http2的io异常中为false,http1则是 true,
//在http1的情况下,需要判定 body有没有实现UnrepeatableRequestBody接口,而body默认是没有实现,所以后续instanceOf不成立,不会走return false.
//We can’t send the request body again.
if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody)
return false;

//todo 3、判断是不是属于重试的异常
//This exception is fatal.
if (!isRecoverable(e, requestSendStarted)) return false;

//todo 4、有没有可以用来连接的路由路线
//No more routes to attempt.
if (!streamAllocation.hasMoreRoutes()) return false;

// For failure recovery, use the same route selector with a new connection.
return true;
}

简单解读一下这个方法:

  1. 如果okhttpClient已经set了不允许重试,那么这里就返回false,不再重试。
  2. 如果requestSendStarted 只在http2.0的IO异常中是true,不过HTTP2.0还没普及,先不管他,这里默认通过。
  3. 判断是否是重试的异常,也就是说,是不是之前重试之后发生了异常。
    这里解读一下,之前重试发生过异常,抛出了Exception,这个isRecoverable方法会根据这个异常去判定,是否还有必要去重试。
  • 协议异常,如果发生了协议异常,那么没必要重试了,你的请求或者服务器本身可能就存在问题,再重试也是白瞎。
  • 超时异常,只是超时而已,直接判定重试(这里requestSendStarted是http2才会为true,所以这里默认就是false)
  • SSL异常,HTTPS证书出现问题,没必要重试。
  • SSL握手未授权异常,也不必重试

private boolean isRecoverable(IOException e, boolean requestSendStarted) {
// 出现协议异常,不能重试
if (e instanceof ProtocolException) {
return false;
}

// requestSendStarted认为它一直为false(不管http2),异常属于socket超时异常,直接判定可以重试
if (e instanceof InterruptedIOException) {
return e instanceof SocketTimeoutException && !requestSendStarted;
}

// SSL握手异常中,证书出现问题,不能重试
if (e instanceof SSLHandshakeException) {
if (e.getCause() instanceof CertificateException) {
return false;
}
}
// SSL握手未授权异常 不能重试
if (e instanceof SSLPeerUnverifiedException) {
return false;
}
return true;
}

  1. 有没有可以用来连接的路由路线,也就是说,如果当DNS解析域名的时候,返回了多个IP,那么这里可能一个一个去尝试重试,直到没有更多ip可用。
重定向

依然是RetryAndFollowUpInterceptor的核心方法 interceptor() 方法,这次我截取后半段:

public Response intercept(Chain chain) throws IOException {
while (true) {
…省略前面的重试判定
//todo 处理3和4xx的一些状态码,如301 302重定向
Request followUp = followUpRequest(response, streamAllocation.route());
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}

closeQuietly(response.body());

//todo 限制最大 followup 次数为20次
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());
}
//todo 判断是不是可以复用同一份连接
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?");
    }
    }
    }

上面源码中, followUpRequest() 方法中规定了哪些响应码可以重定向:

private Request followUpRequest(Response userResponse) throws IOException {
if (userResponse == null) throw new IllegalStateException();
Connection connection = streamAllocation.connection();
Route route = connection != null
? connection.route()
: null;
int responseCode = userResponse.code();

final String method = userResponse.request().method();
switch (responseCode) {
// 407 客户端使用了HTTP代理服务器,在请求头中添加 “Proxy-Authorization”,让代理服务器授权
case HTTP_PROXY_AUTH:
Proxy selectedProxy = route != null
? route.proxy()
: client.proxy();
if (selectedProxy.type() != Proxy.Type.HTTP) {
throw new ProtocolException(“Received HTTP_PROXY_AUTH (407) code while not using proxy”);
}
return client.proxyAuthenticator().authenticate(route, userResponse);
// 401 需要身份验证 有些服务器接口需要验证使用者身份 在请求头中添加 “Authorization”
case HTTP_UNAUTHORIZED:
return client.authenticator().authenticate(route, userResponse);
// 308 永久重定向
// 307 临时重定向
case HTTP_PERM_REDIRECT:
case HTTP_TEMP_REDIRECT:
// 如果请求方式不是GET或者HEAD,框架不会自动重定向请求
if (!method.equals(“GET”) && !method.equals(“HEAD”)) {
return null;
}
// 300 301 302 303
case HTTP_MULT_CHOICE:
case HTTP_MOVED_PERM:
case HTTP_MOVED_TEMP:
case HTTP_SEE_OTHER:
// 如果用户不允许重定向,那就返回null
if (!client.followRedirects()) return null;
// 从响应头取出location
String location = userResponse.header(“Location”);
if (location == null) return null;
// 根据location 配置新的请求 url
HttpUrl url = userResponse.request().url().resolve(location);
// 如果为null,说明协议有问题,取不出来HttpUrl,那就返回null,不进行重定向
if (url == null) return null;
// 如果重定向在http到https之间切换,需要检查用户是不是允许(默认允许)
boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());
if (!sameScheme && !client.followSslRedirects()) return null;

Request.Builder requestBuilder = userResponse.request().newBuilder();
/**

  • 重定向请求中 只要不是 PROPFIND 请求,无论是POST还是其他的方法都要改为GET请求方式,
  • 即只有 PROPFIND 请求才能有请求体
    */

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

实战系列

话不多说,Android实战系列集合都已经系统分类好,由于文章篇幅问题没法过多展示,获取学习笔记链接:点击我的GitHub免费获取


IDOPA-1711092261197)]

实战系列

话不多说,Android实战系列集合都已经系统分类好,由于文章篇幅问题没法过多展示,获取学习笔记链接:点击我的GitHub免费获取

[外链图片转存中…(img-MCiSh6JA-1711092261197)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值