拦截器是 OkHttp
的核心。OkHttp
的网络请求的过程就是依赖于各种拦截器(Interceptor
)实现的,本文将学习okhttp
的拦截器机制。
本文源码分析若无特殊说明,均指 3.14.0
一. 拦截器机制
我们先看看 Interceptor
的定义:
Interceptor .class
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
Interceptor
实际上是一个接口,里面只有一个方法 intercept
以及一个接口 Chain
。
Interceptor
其中,intercept
方法往往是如下的结构:
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// Request阶段,该拦截器在Request阶段负责做的事情
// 调用RealInterceptorChain.proceed(),其实是在递归调用下一个拦截器的intercept()方法
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
// Response阶段,完成了该拦截器在Response阶段负责做的事情,然后返回到上一层的拦截器。
return response;
}
这里先调用了 chain.request
方法获取到了本次请求的 Request
对象,
之后调用了 chain.proceed
方法递归调用下一个拦截器的 interceptor
方法。
最后返回了 chain.proceed
方法所返回的 Response
。
上面简单的三行代码将整个 intercept
过程分为了两个阶段:
-
Request 阶段:执行一些该拦截器在 Request 阶段所负责的事情
-
Response 阶段:完成该拦截器在 Response 阶段所负责的事情
这其实是采用了一种递归的设计,类似我们计算机网络中的分层模型,将 OkHttp
的请求分为了几个阶段,分别代表了不同的拦截器,不同拦截器分别会在这个递归的过程中有两次对该请求的处理的可能,一次是在 Request
之前,一次是在 Response
之后,中间的过程中若出现了错误,则通过抛出异常来通知上层。
预置的 Interceptor
有如下几种:
- RetryAndFollowUpInterceptor:负责实现重定向功能
- BridgeInterceptor:将用户构造的请求转换为向服务器发送的请求,将服务器返回的响应转换为对用户友好的响应
- CacheInterceptor:读取缓存、更新缓存
- ConnectInterceptor:建立与服务器的连接
- CallServerInterceptor:从服务器读取响应
可以看出,整个网络请求的过程由各个拦截器互相配合从而实现,通过这种拦截器的机制,可以很方便地调节网络请求的过程及先后顺序,同时也能够很方便地使用户对其进行扩展。
其中用户可以在两个时机插入 Interceptor:
- 网络请求前后:通过
OkHttpClient.addInterceptor
方法添加 - 读取响应前后:通过
OkHttpClient.addNetworkInterceptor
方法添加
其整体流程如图所示:
RealInterceptorChain
我们再看看是如何通过 RealInterceptorChain
将整个拦截器的调用过程连接起来的,我们先看看其构造过程:
RealInterceptorChain.class
public RealInterceptorChain(List interceptors, Transmitter transmitter,
@Nullable Exchange exchange, int index, Request request, Call call,
int connectTimeout, int readTimeout, int writeTimeout) {
this.interceptors = interceptors;
this.transmitter = transmitter;
this.exchange = exchange;
this.index = index;
this.request = request;
this.call = call;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
this.writeTimeout = writeTimeout;
}
这里只是一些赋值过程,我们接着看到 chain.proceed
方法,看看它是如何执行的:
RealInterceptorChain.class
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
// ...
//再新建一个RealInterceptorChain,这里注意index加1,
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
//获取interceptors列表中的下一个拦截器,并执行intercept方法
Interceptor interceptor = interceptors.get(index);
//调用下一个拦截器的intercept(Chain)方法,传入刚才新建的RealInterceptorChain,返回Response
Response response = interceptor.intercept(next);
// ...
return response;
}
这里省略了一些异常处理,可以看到它首先构造了下一个拦截器对应的 Chain
,之后获取到了当前的拦截器并调用了其 intercept
方法获取其结果,在 intercept
方法的参数中传入的就是下一个拦截器对应的 Chain
。
proceed()
方法中再次新建了一个RealInterceptorChain
,传入了index + 1
,而获取拦截器时是通过index
获取,这样每次都能获取到下一个拦截器,然后调用下一个拦截器的intercept(Chain)
方法,intercept(Chain)
方法中就是拦截器的主要功能实现,里面会继续调用传入的RealInterceptorChain
的proceed()
方法,这样又会重复上述逻辑,我们把拦截器看作一条链中的节点,这样每个拦截器就通过一个个RealInterceptorChain
连接起来,形成一条链,这就是典型的责任链模式,从节点的首部开始把请求传递下去,每一个拦截器都有机会处理这个请求,这又像是一个递归的过程,直到最后一个拦截器器处理完请求后,才开始逐层返回Resquese
,拦截器才是Okhttp
核心功能所在。
小结
OkHttp
在读取响应的过程中采用了一种责任链模式,预置了多个负责不同功能的拦截器,将它们通过责任链连接在一起,采用了一种递归的方式进行调用,从而使得每一层在请求前和响应后都能对本次请求作出不同的处理,通过各个拦截器的协调合作,最终完成了整个网络请求的过程。
二.自定义拦截器的使用
从一中可知,Interceptor由两部分组成:intercept(Chain)方法和内部接口Chain,下面是自定义一个拦截器的通用逻辑,如下:
public class MyInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
//1、通过传进来的Chain获取Request
Request request = chain.request();
//2、 处理Request,逻辑自己写
//...
//3、调用Chain的proceed(Request)方法处理请求,得到Response
Response response = chain.proceed(request);
//4、 处理Response,逻辑自己写
//...
//5、返回Response
return response;
}
}
上述就是一个拦截器的通用逻辑,首先我们继承Interceptor实现intercept(Chain)方法,完成我们自己的拦截逻辑,即根据需要进行1、2、3、4、5步,不管是自定义拦截器还是后面介绍的okhttp默认的拦截器大概都是这个模板实现,定义完拦截器后,我们在构造OkhttpChient时就可以通过addInterceptor(Interceptor)或addNetworkInterceptor(Interceptor)添加自定义拦截器,如下:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new MyInterceptor())
.build();
或
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new MyInterceptor())
.build();
这样okhttp在链式调用拦截器处理请求时就会调用到我们自定义的拦截器,那么addInterceptor(Interceptor)和addNetworkInterceptor(Interceptor)有什么不一样呢?它们一个是添加应用拦截器,一个是添加网络拦截器,主要是调用的时机不一样,更多区别可以参考官方WIKI文档Okhttp-wiki 之 Interceptors 拦截器点击跳转,当我们平时做应用开发使用addInterceptor(Interceptor)就行了。
上述是我们自定义的拦截器,下面我们来看看okhttp默认的拦截器都干了什么。
三.大致流程分析
1. RetryAndFollowUpInterceptor
对该类的描述如下:
/**
* This interceptor recovers from failures and follows redirects as necessary. It may throw an
* {@link IOException} if the call was canceled.
*
* 此拦截器从故障中恢复,并根据需要进行重定向。如果呼叫被取消,则可能抛出IOException .
*/
我们看到 RetryAndFollowUpInterceptor.intercept
方法
private static final int MAX_FOLLOW_UPS = 20;
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
// 获取transmitter (发射机)
Transmitter transmitter = realChain.transmitter();
int followUpCount = 0;
Response priorResponse = null;
while (true) {
// 进行一些连接前的准备工作
transmitter.prepareToConnect(request);
// 处理取消事件
if (transmitter.isCanceled()) {
throw new IOException("Canceled");
}
Response response;
boolean success = false;
try {
// 调用chain的proceed方法获取下层得到的结果
response = realChain.proceed(request, transmitter, null);
success = true;