1.概述
通过上一章节的学习,我们明白了OkHttp拦截器链的调用原理,对拦截器有了初步的概念:OkHttp的网络请求,通过拦截器的链式调用,完成了与服务器端的数据交互。本章开始我们将学习每一个拦截器,今天我们要讲的,是用户可传入的拦截器。我个人觉得拦截器可以归为两大类,一大类是我们可以通过代码传递给OkHttpClient的拦截器(就是我们今天要讲的拦截器),另一大类是系统内部的拦截器。用户可传入的拦截器,分为两种:Application拦截器、Network拦截器。
我们先看一下官网对拦截器的概括
Interceptors are a powerful mechanism that can monitor, rewrite, and
retry calls.
Interceptors是一种强大的机制,它可以监控、重写、重定向calls。
2.Application拦截器
2.1.使用方式
Application拦截器,应用的拦截器。它的使用方式:
- 自定义一个拦截器类,实现Interceptor接口;
- 调用OkHttpClient.Builder的addInterceptor(Interceptor)方法,将自定义的拦截器传入。
代码说明
/**
*自定义拦截器
*/
public class LoggingInterceptor implements Interceptor {
private final String TAG = "OkHttp-Interceptor";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.nanoTime();
Log.d(TAG, String.format("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long endTime = System.nanoTime();
Log.d(TAG, String.format("Receive response for %s in %.1fms%n%s",
response.request().url(), (endTime - startTime) / 1e6d, response.headers()));
return response;
}
}
看下如何将一个拦截器作为Application Interceptor使用:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor())//这句代码决定了加入的是Application Interceptor
.build();
其实Application Interceptor的关键就在于添加拦截器的方法是addInterceptor(Interceptor)而不是addNetworkInterceptor(Interceptor),注意到这一点,那就OK啦。接下来我们看一下官网对Application Interceptor的几点建议
2.2.理解
1· Don’t need to worry about intermediate responses like redirects and
retries.
(不需要担心中间的相应结果,比如说重定向、重试);
2· Are always invoked once, even if the HTTP response is served from
the cache.
(任何情况下只会调用一次,即使这个Http响应结果是由缓存提供的);
3· Observe the application’s original intent.Unconcerned with OkHttp-injected headers like
If-None-Match.
(可以监听这个请求最原始的请求头请求体,不可操作OkHttp为我们添加的额外的请求头,比如说If-None-Match);
4· Permitted to short-circuit and not call Chain.proceed().
(允许短路,并且允许不调用Chain.proceed()方法);
5· Permitted to retry and make multiple calls to Chain.proceed().
(允许失败重试,多次调用Chain.proceed()方法);
上面的官网的总结,我翻译的不是很好,读起来比较难以理解,我们利用源码讲道理的方法,跟大家挨个讲讲道理,这样就可以理解了。
我们先看第一个总结“不需要担心中间的相应结果,比如说重定向、重试”,我个人觉得,它的意思是想说Application拦截器,无法做Request和Response的中间操作,因为Application Interceptor处于拦截器链的最顶端,意味着它是第一个被调用的拦截器,在请求的过程中对Request的处理和在响应过程中对Response的处理,都是由后面的那些系统拦截器或者Network拦截器做的。我们通过源码看下
//addInterceptor方法将Application拦截器存入了interceptors集合中,可通过OkHttpClient的interceptors()方法获取
public Builder addInterceptor(Interceptor interceptor) {
if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
interceptors.add(interceptor);
return this;
}
//getResponseWithInterceptorChain()方法中,集合中最早添加的拦截器,就是Application Interceptor
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
//首先将Application拦截器加入到集合中
interceptors.addAll(client.interceptors());
...
}
从代码中我们可以看到,Application 拦截器,的确是最早被添加到拦截器集合中的,根据我们上一章节拦截器链的讲解,按照拦截器链的调用顺序,Application Interceptor的确是最早被调用的,所以它拿到的是最原始的Request和最终处理完毕的Response,而Request的处理和Response的处理,中间那些重定向、重试、缓存等操作,都是由后面的拦截器一层层的完成的。
“任何情况下只会调用一次,即使这个Http响应结果是由缓存提供的”这句话也不难理解,指的是Application Interceptor的intercept(Chain)方法只被调用了一次。我们应该能想明白,因为它是第一个拦截器,所以只会在最初的时候,拦截器链的index为0的时候调用的第一个拦截器就是它,后面当然不会再被调用了。
“可以监听这个请求最原始的请求头请求体,不可操作OkHttp为我们添加的额外的请求头,比如说If-None-Match”关于这句话,前半句不难理解,因为是最早被调用的一个拦截器,所以肯定是最原始的请求头请求体,但是后半句关于If-None-Match我自己也不是太明白。我是这样理解的,类似于“If-None-Match”这样的请求头,是由OkHttp框架额外自己添加的,我们是不被允许去修改这些请求头的。其中提到的If-None-Match请求头,是HTTP请求中一种常用的判断资源是否改变的方法,判断本次请求与上次请求到的资源是否一致,可以用于网络请求缓存,当经过判断前后两次请求资源未改变则直接读取缓存中的数据即可。
“允许短路,并且允许不调用Chain.proceed()方法”这个我们可以这样理解:客户端要求的是获取到Response,如果我们本地有缓存的话,Chain.proceed()方法可以不去从服务器端获取Response而直接从缓存中获取并且返回给用户,这也就是短路。
“允许失败重试,多次调用Chain.proceed()方法”,在Application Interceptor中如果检测到请求失败了,可以多次调用Chain.proceed()方法,做重试操作,这是被允许的。
2.3.总结
我们简单总结下Application的使用方法及使用场景:
- Application Interceptor使用OkHttpClient.Builder的addInterceptor()方法添加拦截器;
- Application Interceptor 是最早被调用的拦截器,并且任何情况下只会被调用一次。
- Application Interceptor我们可以用来做一些原始Request、最终Response的统一处理,比如说请求前添加一些统一参数,监控整个请求耗费的时间等等。
3.Network拦截器
3.1.使用方式
Network拦截器的使用方式:
- 创建一个自定义拦截器实现Interceptor接口;
- 调用OkHttpClient.Builder的addNetworkInterceptor(Interceptor)方法添加拦截器。
上代码:
我们自定义拦截器,还是继续使用上面创建的LoggingInterceptor,我们看一下添加拦截器的代码即可
//使用addNetworkInterceptor添加Network拦截器
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new LoggingInterceptor())
.build();
同样的我们需要注意的是,如果我们使用的是Network Interceptor,则一定要使用addNetworkInterceptor(Interceptor)方法来添加拦截器,需要注意的是,Network拦截器可能会出现多次被调用的情况,因为他前面有重定向拦截器,一旦重新请求,那么就会再次调用Network拦截器。我们继续来看官网对它的一些解释
3.2.理解
1· Able to operate on intermediate responses like redirects and retries.
(可以操作中间响应,如重定向、重试);
2· Not invoked for cached responses that short-circuit the network.
(不允许调用缓存来短路这个请求);
3· Observe the data just as it will be transmitted over the network.
(观察数据在网络中的传输);
4· Access to the Connection that carries the request.
(可以获得携带着Request的Connection的使用权)
跟上面的Application一样,官网上的东西我的确翻译不好,不太好懂,我们通过源码讲道理,来理解一下,我们先上一下源码
//addNetworkInterceptor方法将Network拦截存入了networkInterceptors集合中
public Builder addNetworkInterceptor(Interceptor interceptor) {
if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
networkInterceptors.add(interceptor);
return this;
}
我们继续看一下看了不知多少遍的getResponseWithInterceptorChain()方法
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
// 添加重定向拦截器
interceptors.add(retryAndFollowUpInterceptor);
//添加桥接拦截器
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//添加缓存拦截器
interceptors.add(new CacheInterceptor(client.internalCache()));
//添加建立连接的拦截器
interceptors.add(new ConnectInterceptor(client));
//当网络请求不是WebSocket的时候
if (!forWebSocket) {//将Network拦截器添加到拦截器列表中
interceptors.addAll(client.networkInterceptors());
}
//添加与服务器进行数据传输的拦截器
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
通过上面代码的解读,我们需要明白:Network Interceptor在拦截器列表中的顺序倒数第二,根据拦截器链的调用顺序,我们可以明白,重定向、设置请求头、缓存、与服务器建立连接等工作,在Network拦截器被调用之前都已经被调用了,在Network之后,就是调用与服务器进行数据传输的CallServerInterceptor了。所以在与服务器进行数据传输之前,最后一步经过的就是Network拦截器,获取到服务器端的响应之后,Response会第一时间递交给Network Interceptor。我们通过下面这幅图来更加形象的说明一下:
“可以操作中间响应,如重定向、重试”很明显,重定向、重试等操作是在重定向拦截器RetryAndFollowUpInterceptor等拦截器中完成的,在Network拦截器之前,所以Network完全可以操作这些请求、响应。
“不允许调用缓存来短路这个请求”,如果这个请求时短路请求,也就是说从缓存中获取了Response,那么拦截器链截止CacheInterceptor(缓存拦截器)为止,就不会继续往下走了,CacheInterceptor会将缓存的Response回传,所以Network 拦截器是无法操作缓存请求、响应的。
“观察数据在网络中的传输”,前面我们说过,在于服务器端进行数据传输之前,最后一步会经过Network Interceptor,从服务器端返回响应结果Response时,首先会回传给Network Interceptor,所以Network拦截器是观察数据在网络中传输的最佳选择,可以看到最终的请求信息和最初获取到的请求结果。
“可以获得携带着Request的Connection的使用权”,Connection是在ConnectInterceptor中创建并与服务器建立连接,然后存入拦截器链中,所以Network Interceptor可以获取到Connection,Chain.connection()方法可以获得,并使用。
3.3.总结
我们总结下Network Interceptor的使用及场景
- 用户必须通过OkHttpClient.Builder的addNetworkInterceptor()方法添加的拦截器才是Network 拦截器;
- 如果网络请求时WebSocket则无法添加Network 拦截器,Network拦截器不能操作缓存请求或者响应;
- 网络请求与服务器数据传输之前,最后会经过Network 拦截器,从服务器获取到结果Response之后,首先会回传给Network 拦截器。
- Network拦截器可以用于观察数据在网络中的传输,精准记录HTTP请求过程,比如使用Network 拦截器做网络上传和下载进度的监听。
这就是今天要讲的用户可以传入的拦截器,Application拦截器和Network拦截器,希望各位大神多多指点。下一章节我们开始介绍OkHttp系统的那五个拦截器。