概述
在 OkHttp 中,拦截器是非常重要的一个知识点,它将整个请求网络的过程的每一步都封装在不同的 Interceptor 里。这里使用到了设计模式中的责任链模式,即每个拦截器只处理与自己相关的业务逻辑。
OkHttp 不仅提供了 5 个固定的拦截器,而且我们还可以添加自定义的拦截器。就想在OkHttp 使用指南 – 基本使用 中介绍的添加 HttpLoggingInterceptor
一样。
我们先来看一下 OkHttp 提供的5个基础拦截器:
- RetryAndFollowUpInterceptor:重试及重定向拦截器,负责请求的重试和重定向。
- BridgeInterceptor:桥接拦截器,给请求添加对应的 header 信息,处理响应结果的 header 信息。
- CacheInterceptor:缓存拦截器,根据当前获取的状态选择 网络请求 、读取缓存、更新缓存。
- ConnectInterceptor:连接拦截器,建立 http 连接。
- CallServerInterceptor:读写拦截器,读写网络数据。
有了这些拦截器,我们才能通过 OkHttp 进行网络的请求,这里先做一个概念性的了解,后面会做详细的介绍。
工作流程
创建拦截器
在我们创建网络请求任务后,不管是同步请求还是异步请求,网络请求的结果都是通过 getResponseWithInterceptorChain()
方法来获得的:
1 | @Override public Response execute() throws IOException { |
1 | final class AsyncCall extends NamedRunnable { |
接下来,我们就研究一下 getResponseWithInterceptorChain()
方法。
1 | Response getResponseWithInterceptorChain() throws IOException { |
它的工作流程大致分为两个部分:
1.创建一系列的拦截器,包括用户自定义拦截器和框架提供的基础拦截器。
2.创建一个拦截器链 RealInterceptorChain
,调用 proceed
方法开始执行拦截器。
在这里创建 RealInterceptorChain
时,StreamAllocation、HttpCodec 和 RealConnection 参数都为 null,不用担心,这些参数在需要的时候会在后面的拦截器里面创建相应的对象。
拦截器链
拦截器链的作用就是负责按照拦截器的添加顺序执行拦截器定义的相关操作,来获取服务器的响应返回。
我们先来分析一下拦截器链的主要代码:
1 | public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation, |
1.首先判断一下拦截器是否执行完毕,执行完毕就抛出一个 AssertionError 异常,一般不会满足这个条件。
2.为下一个拦截器创建一个新的拦截器链。
3.从拦截器数组中取出当前需要执行的拦截器,开始执行拦截器的实现 intercept
方法,并把创建的下一个拦截器链作为参数传递进去。获取拦截器的返回结果。
4.从 intercept
开始就会递归调动拦截器数组里面所有的拦截器。在拦截器的 intercept
里面一般会在调用新创建的拦截器链的 proceed
方法之前(请求信息)或者之后(响应信息)加入自己需要执行的代码。直到调用最后一个拦截器 CallServerInterceptor
,由于是最后一个拦截器,它的 intercept
不会再调用参数 chain
的 proceed
方法。
5.执行完后就再一层一层的返回响应信息。
其实看我后我们就会觉得 OkHttp 的实现其实挺简单,就是这些链接器的执行过程。下面开始简单介绍一下拦截器。
拦截器
拦截器的执行一般为三个部分:
- 在发起请求前对request进行处理。
- 调用下一个拦截器,获取response。
- 对response进行处理,返回给上一个拦截器。
这就是 OkHttp 拦截器机制的核心逻辑。所以一个网络请求实际上就是一个个拦截器执行其 intercept 方法的过程。
拦截器关键的代码就是 chain.proceed()
方法的执行,我们知道在下一个拦截器链中又会执行下一个拦截器的intercept方法。所以整个执行链就在拦截器与拦截器链中交替执行,最终完成所有拦截器的操作。这也是 OkHttp 拦截器的链式执行逻辑。
RetryAndFollowUpInterceptor
重试及重定向拦截器,负责请求的重试和重定向。主要是初始化的一些工作,创建 StreamAllocation 对象,用来传递给后面的拦截器。
1 | @Override public Response intercept(Chain chain) throws IOException { |
recover
方法用来判断是否要重新进行网络请求。
1 | private boolean recover(IOException e, StreamAllocation streamAllocation, |
BridgeInterceptor
桥接拦截器主要进行下面的工作:
- 将用户的 Request 信息构建一个能进行网络访问的请求,并给请求添加缺少的一些必要的 header 信息。
- 处理响应结果的 header 信息。
1 | @Override public Response intercept(Chain chain) throws IOException { |
CacheInterceptor
缓存拦截器,根据当前获取的状态选择 网络请求 、读取缓存、更新缓存。
ConnectInterceptor
连接拦截器,建立 http 连接。是 OkHttp 核心拦截器,网络交互的关键。
1 | @Override public Response intercept(Chain chain) throws IOException { |
ConnectInterceptor 的执行流程:
1 | ├── ConnectInterceptor.intercept |
注意:这里直接调用的是 RealInterceptorChain
重载的 proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection)
方法,传递的都是非空的参数,和在 RealCall
的 getResponseWithInterceptorChain()
方法以及前面的拦截器里面调用的 proceed
有所区别。
CallServerInterceptor
读写拦截器,读写网络数据。主要负责将请求写入到 IO 流当中,并且从 IO 流当中获取服务端返回给客服端的响应数据,它也是 OkHttp 核心拦截器,网络交互的关键。