本平台的文章更新会有延迟,大家可以关注微信公众号-顾林海,包括年底前会更新kotlin由浅入深系列教程,目前计划在微信公众号进行首发,如果大家想获取最新教程,请关注微信公众号,谢谢
前面几节介绍了OkHttp的同步和异步请求的整体流程以及Dispatcher分发器的作用,接下来介绍一下OkHttp中的五个拦截器。
RetryAndFollowUpInterceptor拦截器
RetryAndFollowupInterceptor是重试重定向拦截器,它的主要作用是负责失败重连。OkHttp中的重定向功能是默认开启的。
该拦截器方法如下:
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
...
//标记1
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
...
while (true) {
//取消,释放
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
...
//标记2
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
...
Request followUp;
//标记3
followUp = followUpRequest(response, streamAllocation.route());
...
//标记4
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
...
}
}
在intercept方法中省略了一些代码,我们只看核心代码:
在标记1处创建了StreamAllocation对象,创建的StreamAllocation对象在RetryAndFollowupInterceptor拦截器中并没有使用到。
StreamAllocation对象会通过标记2处的proceed方法传递给下一个拦截器,直到ConnectInterceptor拦截器,StreamAllocation的主要作用是提供建立HTTP请求所需要的网络组件,ConnectInterceptor从RealInterceptorChain获取前面的Interceptor传过来的StreamAllocation对象,执行StreamAllocation对象的newStream()方法完成所有的连接建立工作,并将这个过程中创建的用于网络IO的RealConnection对象,以及与服务器交互最为关键的HttpCodec等对象传递给后面的CallServerInterceptor拦截器。
在标记3处,会对返回的Response进行检查,通过followUpRequest方法对Response返回的状态码判断,并创建重定向需要发出的Request对象。
followUpRequest代码如下:
private Request followUpRequest(Response userResponse, Route route) throws IOException {
if (userResponse == null) throw new IllegalStateException();
int responseCode = userResponse.code();
final String method = userResponse.request().method();
switch (responseCode) {
case HTTP_PROXY_AUTH://407 代理服务器认证
...
case HTTP_UNAUTHORIZED://401 身份未认证
...
case HTTP_PERM_REDIRECT://308
case HTTP_TEMP_REDIRECT://307
//当请求的method不为GET和HEAD时不进行重定向
...
case HTTP_MULT_CHOICE://300 多种选择
case HTTP_MOVED_PERM://301 永久移除
case HTTP_MOVED_TEMP://302 临时重定向
case HTTP_SEE_OTHER://303 其他问题
...
case HTTP_CLIENT_TIMEOUT://408 请求超时
...
case HTTP_UNAVAILABLE://503 服务不可用
...
default:
return null;
}
}
followUpRequest方法主要是对返回的状态码进行判断,根据特定的状态码创建重定向需要的Request对象。
回到上面拦截器intercept方法,在标记4判断followUpCount是否大于MAX_FOLLOW_UPS(20),也就是说重定向次数上限为20,当重试次数超过20的时候,会释放StreamAllocation这个对象,这样做是为了防止无限制的重试网络请求,从而造成资源的浪费,关于重定向的次数建议可以按照Chrome遵循21个重定向;Firefox、CURL和WGET遵循20;Safari遵循16;HTTP/1推荐5。
总结RetryAndFollowupInterceptor拦截器:
-
创建StreamAllocation对象。
-
调用RealInterceptorChain.proceed()进行网络请求。
-
根据异常结果或响应结果判断是否进行重新请求。
-
调用下一个拦截器,对Response进行处理,返回给上一个拦截器。
BridgeInterceptor拦截器
BridgeInterceptor是桥接和适配拦截器,它的作用是设置内容长度、编码方式以及压缩等等一系列操作,主要是添加头部的作用。
该拦截器方法如下:
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
//====================添加头部信息========================
RequestBody body = userRequest.body();
...
if (userRequest.header("Connection") == null) {
//标记1
requestBuilder.header("Connection", "Keep-Alive");
}
...
//标记2
Response networkResponse = chain.proceed(requestBuilder.build());
//标记3
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
//标记4
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
//标记5
GzipSource responseBody = new GzipSource(networkResponse.body().source());
...
}
return responseBuilder.build();
}
intercept方法主要是给Request添加一些头部信息,在标记1处的Connection设置为Keep-Alive,Keep-Alive的作用是当我们开启一个HTTP连接后,在一定时间内保持它的连接状态。
在标记2处调用了拦截器链的proceed方法向服务器发起请求。
在标记3处通过HttpHeaders的receiveHeaders方法,通过网络请求,服务器返回的Response转化为客户端可以识别的Response,如果HTTP默认支持gzip,那么BridgeInterceptor拦截器将会对这个Response进行解压,最终得到客户端使用的Response。
在标记4处,对transparentGzip标志位进行判断,如果transparentGzip为true就表面Accept-Encoding支持gzip压缩;再判断头部的Content-Encoding是否为gzip,保证服务端返回的响应体内容是经过了gzip压缩的;最后判断HTTP头部是否有Body。当满足这些条件后在标记5处将Response的body转换为GzipSource类型,这样的话client端直接以解压的方式读取数据。
总结BridgeInterceptor拦截器:
-
负责将用户构建的一个Request请求转化为能够进行网络访问的请求,通过给头部添加信息。
-
将这个符合网络请求的Request进行网络请求。
-
将网络请求回来的响应Response转化为用户可用的Response。
838794-506ddad529df4cd4.webp.jpg
搜索微信“顾林海”公众号,定期推送优质文章。
网友评论