1. 秦子帅的博客首页
  2. Android

OkHttp3-拦截器解析(1)

前言

上次写了一篇OkHttp3源码解析(整体流程),今天继续讲一下拦截器,今天主要讲三部分:
  • 拦截器与设计模式
  • RetryAndFollowUpInterceptor解析
  • BridgeInterceptor解析

拦截器与设计模式

上节我们讲了okhttp的整体的流程,里面的核心方法之一是getResponseWithInterceptorChain() ,这个方法应该知道吧?通过拦截器层层处理返回Response;这个方法中其实应用了责任链设计模式。今天主要讲一下它是如何应用的!

1.责任链设计模式

责任链模式的定义
在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
模型:
OkHttp3-拦截器解析(1)
优点
耦合度降低,请求和处理是分开的
缺点
责任链太长或者每条链判断处理的时间太长会影响性能。特别是递归循环的时候
不一定被处理,每个职责类的职责很明确,这就需要对写默认的处理了
责任链模式重要的两点:分离职责,动态组合
对责任链设计模式不明白的可以去网上那个找找实例看看, 这里就不举例子了。

2.源码中的责任链

话不多说,直接上getResponseWithInterceptorChain() 源码
OkHttp3-拦截器解析(1)
方法中大部分上节已经说了,就是 List<Interceptor>添加自定义、cookie等等的拦截器,今天我们主要看看后半部分:OkHttp3-拦截器解析(1)
首先初始化了 RealInterceptorChainRealInterceptorChainInterceptor.Chain的实现类OkHttp3-拦截器解析(1)
先看一下Interceptor.Chain
OkHttp3-拦截器解析(1)
生成了RealInterceptorChain的实例,调用了proceed(),返回了最后的Response
我们看下 RealInterceptorChain类中的proceed()
OkHttp3-拦截器解析(1)
经过一系列的判断,看下proceed()的核心

1
2
3
4
5
6
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

 

如果对责任链模式有认识的朋友看到上面代码,应该直接就能看出来了,并能分析出:
  • 如果是责任链模式, 那个intercept()一定是关键, Interceptor是接口,interceptors集合中的拦截器类肯定都实现了 Interceptor以及 interceptor.intercept()
  • 每个拦截器 intercept()方法中的chain,都在上一个 chain实例的 chain.proceed()中被初始化,并传递了拦截器List与 index,直接最后一个 chain实例执行即停止。
OkHttp3-拦截器解析(1)
3.总结:
每个chaininterceptorsindex都是由上一个chain初始化传递过来的,在chain.proceed()中获取对应的interceptor实例 并初始化下一个chain,直到最后一个chain被执行。
这样就清晰了吧?责任链模式的重点在“链上”,由一条链去处理相似的请求,在链中决定谁来处理这个请求,并返回相应的结果。

RetryAndFollowUpInterceptor解析

如果我们没有去自定义拦截器, 那RetryAndFollowUpInterceptor是第一个拦截器。

1.初始化

首先先看RetryAndFollowUpInterceptor被添加的位置:
OkHttp3-拦截器解析(1)

初始化位置:
call实例化方法中:

1
2
3
4
5
6
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}

 

找到了初始化的位置, 下面去RetryAndFollowUpInterceptor种分析!
2. 解析
从上面我们就知道拦截器中的intercept()是核心! 这里贴出代码:
OkHttp3-拦截器解析(1)

 

我先贴出一个while循环的流程图:
OkHttp3-拦截器解析(1)
根据流程图和源码可以分析RetryAndFollowUpInterceptor主要做了以下内容,后两点都是发生在while循环中:
  • 初始化StreamAllocation 对象
  • 网络请求-chain.proceed() ,对在请求时发生的异常进行捕获以及对应的重连机制
  • followUpRequest 对响应码进行处理
下面可以逐块代码分析:
(1).初始化StreamAllocation 对象
StreamAllocation类是协调三个实体之间的关系 三个实体是:ConnectionsStreamsCalls
我们请求网络时需要传递它
OkHttp3-拦截器解析(1)
StreamAllocation在这大家简单了解一下就可以了.
(2).网络请求时异常捕获-以及重连机制
网络请求如下:
response = realChain.proceed(request, streamAllocation, null, null);
如果请求发现异常,我们通过try/catch捕获

OkHttp3-拦截器解析(1)

RouteException 路由异常
  • IOException IO异常
    捕获后都做了recover()重连判断,具体代码如下,就不细说了:

OkHttp3-拦截器解析(1)

这里需要注意的是如果可以重连,执行 continue;
continue含义:继续循环,(不执行 循环体内continue 后面的语句,直接进行下一循环)
(3).followUpRequest 对响应码进行处理
先看看具体followUpRequest方法:
OkHttp3-拦截器解析(1)
不难看出, 是根据响应码进行判断的。
  • HTTP_PROXY_AUTH 407 代理身份验证
  • HTTP_UNAUTHORIZED 401 未授权
  • HTTP_PERM_REDIRECT 308 重定向
  • HTTP_TEMP_REDIRECT 307 重定向
  • HTTP_MULT_CHOICE 300 Multiple Choices
  • HTTP_MOVED_PERM 301 Moved Permanently
  • HTTP_MOVED_TEMP 302 Temporary Redirect
  • HTTP_SEE_OTHER 303 See Other
  • HTTP_CLIENT_TIMEOUT 408 Request Time-Out
  • HTTP_UNAVAILABLE 503 Service Unavailable
对于这些响应码都做了处理:
(1).返回null

1
2
3
4
5
6
if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }
(2).其他异常情况直接抛异常了
强调:
MAX_FOLLOW_UPS字段, 表示最大的重定向次数
private static final int MAX_FOLLOW_UPS = 20;
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}

 

BridgeInterceptor解析

今天讲一下BridgeInterceptor,我们先看一下源码对此类的解释:

/**
* Bridges from application code to network code. First it builds a network request from a user
* request. Then it proceeds to call the network. Finally it builds a user response from the network
* response.
*/

意思就是从应用程序代码到网络代码的桥梁
  • 它从用户请求构建成网络请求
  • 然后进行网络请求
  • 将返回的响应数据转用户可用的response
下面贴出BridgeInterceptor 代码:
OkHttp3-拦截器解析(1)
BridgeInterceptor初始化方法中也实例了cookieJarcookieJar代码如下:
public interface CookieJar {
/** A cookie jar that never accepts any cookies. */
CookieJar NO_COOKIES = new CookieJar() {
@Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
}
@Override public List<Cookie> loadForRequest(HttpUrl url) {
return Collections.emptyList();
}
};
/**
* Saves {@code cookies} from an HTTP response to this store according to this jar's policy.
*
* <p>Note that this method may be called a second time for a single HTTP response if the response
* includes a trailer. For this obscure HTTP feature, {@code cookies} contains only the trailer's
* cookies.
*/
void saveFromResponse(HttpUrl url, List<Cookie> cookies);
/**
* Load cookies from the jar for an HTTP request to {@code url}. This method returns a possibly
* empty list of cookies for the network request.
*
* <p>Simple implementations will return the accepted cookies that have not yet expired and that
* {@linkplain Cookie#matches match} {@code url}.
*/
List<Cookie> loadForRequest(HttpUrl url);
}

 

发现cookieJar就是个接口,里面有两个方法:
  • saveFromResponse 自定义去存储cookie
  • loadForRequest 获取指定URL的cookie
我们在请求时需要配置一些东西,例如header等等,例如下图:
OkHttp3-拦截器解析(1)

 

这就很好理解了吧?我们继续看BridgeInterceptor源码:
所以BridgeInterceptor 第一步就是给rquest设置header等配置,这块主要强调的是:

List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

我们可以调用loadForRequest获取我们自定义的cookie ,设置到header中。
设置之后,进行网络请求:

Response networkResponse = chain.proceed(requestBuilder.build());

很熟悉了,这里就不讲了。
最后就是将返回的networkResponse 转用户可用的response ,这里需要注意的是:
1.接收header信息,存储cookie

HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

OkHttp3-拦截器解析(1)

gzip解压
如果服务器返回的networkResponse 是压缩过的,需要GzipSource解压,读取response

OkHttp3-拦截器解析(1)

这节就说到这,还有其他的拦截器我们下节说,希望对大家有所帮助…..

发布者:秦子帅,转转请注明出处:http://qinzishuai.cn/6867/ac32f4315a/

联系我们

912241847

在线咨询:点击这里给我发消息

邮件:qzs531156@163.com

QR code