OKHTTP结合官网示例分析两种自定义拦截器的区别

OKHTTP异步和同步请求简单分析
OKHTTP拦截器缓存策略CacheInterceptor的简单分析
OKHTTP拦截器ConnectInterceptor的简单分析
OKHTTP拦截器CallServerInterceptor的简单分析
OKHTTP拦截器BridgeInterceptor的简单分析
OKHTTP拦截器RetryAndFollowUpInterceptor的简单分析
OKHTTP结合官网示例分析两种自定义拦截器的区别

在了解自定义拦截器之前,先来看一下 OKHTTP 官网提供的示例代码块,LoggingInterceptor 是用于在网络请求期间的日志记录拦截器。

1、LoggingInterceptor示例代码

通过下面的代码可以看出,它就是用于在请求发送前网络响应后的打印日志信息的拦截器。
LoggingInterceptor 中主要做了 3 件事:

  • 1.请求前-打印请求信息;
  • 2.网络请求-递归去调用其他拦截器发生网络请求;
  • 3.网络响应后-打印响应信息;
class LoggingInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request request = chain.request();
    
    //1.请求前--打印请求信息
    long t1 = System.nanoTime();
    logger.info(String.format("Sending request %s on %s%n%s",
        request.url(), chain.connection(), request.headers()));

    //2.网络请求
    Response response = chain.proceed(request);

    //3.网络响应后--打印响应信息
    long t2 = System.nanoTime();
    logger.info(String.format("Received response for %s in %.1fms%n%s",
        response.request().url(), (t2 - t1) / 1e6d, response.headers()));

    return response;
  }
}

2、Application Intercetor和NetworkInterceptor的区别

2.1 拦截器调用流程

在官方文档中OKHTTP官网示例LoggingInterceptor 作为示例,解释了 Application IntercetorNetworkInterceptor 的区别。

在看两种拦截器的区别之前,还是要点击上面的链接去看看,之后再看这个博客的总结会好点。到这里,默认就是已经看过了官网的示例了,那我们就使用一个图来看看拦截器的调用过程:

拦截器的执行过程.png

从上面的调用关系可以看出除了红色圈出的拦截器之外都是系统提供的拦截器,这整个过程是递归的执行过程,在 CallServerInterceptor 中得到最终的 Response 之后,将 response 按递归逐级进行返回,期间会经过 NetworkInterceptor 最后到达 Application Interceptor

2.1 给网络请求添加拦截器

  • 添加 Application Interceptor
OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new LoggingInterceptor())
    .build();

Request request = new Request.Builder()
    .url("http://www.publicobject.com/helloworld.txt")
    .header("User-Agent", "OkHttp Example")
    .build();

Response response = client.newCall(request).execute();
response.body().close();
响应数据:
INFO: Sending request http://www.publicobject.com/helloworld.txt on null
User-Agent: OkHttp Example

INFO: Received response for https://publicobject.com/helloworld.txt in 1179.7ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive
  • 添加 NetworkInterceptor
OkHttpClient client = new OkHttpClient.Builder()
    .addNetworkInterceptor(new LoggingInterceptor())
    .build();

Request request = new Request.Builder()
    .url("http://www.publicobject.com/helloworld.txt")
    .header("User-Agent", "OkHttp Example")
    .build();

Response response = client.newCall(request).execute();
response.body().close();
INFO: Sending request http://www.publicobject.com/helloworld.txt on Connection{www.publicobject.com:80, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=none protocol=http/1.1}
User-Agent: OkHttp Example
Host: www.publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzip

INFO: Received response for http://www.publicobject.com/helloworld.txt in 115.6ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/html
Content-Length: 193
Connection: keep-alive
Location: https://publicobject.com/helloworld.txt

INFO: Sending request https://publicobject.com/helloworld.txt on Connection{publicobject.com:443, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA protocol=http/1.1}
User-Agent: OkHttp Example
Host: publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzip

INFO: Received response for https://publicobject.com/helloworld.txt in 80.9ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive

在官网给出的 NetworkInterceptorApplication Interceptor 的区别在上面的图中就可以看出:

  • Application Interceptor 是第一个 Interceptor 因此它会被第一个执行,因此这里的 request 还是最原始的。而对于 response 而言呢,因为整个过程是递归的调用过程,因此它会在 CallServerInterceptor 执行完毕之后才会将 Response 进行返回,因此在 Application Interceptor 这里得到的 response 就是最终的响应,虽然中间有重定向,但是这里只关心最终的 response
  • NetwrokInterceptor 处于第 6 个拦截器中,它会经过 RetryAndFollowIntercptor 进行重定向并且也会通过 BridgeInterceptor 进行 request 请求头和 响应 resposne 的处理,因此这里可以得到的是更多的信息。在打印结果可以看到它内部是发生了一次重定向操作,在上图可以看出,为什么 NetworkInterceptor 可以比 Application Interceptor 得到更多的信息了。

Application Interceptor 适用于在请求前统一添加一些公共参数,例如在添加 APP 的版本号,用户 ID ,手机版本号,运营商类型等参数。或者对响应体的数据进行 json 转化等操作。

NetwrokInterceptor 在这一层拦截器中可以获取到最终发送请求的 request ,也可以获取到真正发生网络请求回来的 response 响应,从而修改对应的请求或者响应数据。

3、两种拦截器的区别

下面是官网给出的区别,根据我自己的理解,使用中文标出了自己的解释。

Application interceptors

  • Don't need to worry about intermediate responses like redirects and retries.
    不需要去关心中发生的重定向和重试操作。因为它处于第一个拦截器,会获取到最终的响应 response 。
  • Are always invoked once, even if the HTTP response is served from the cache.
    只会被调用一次,即使这个响应是从缓存中获取的。
  • Observe the application's original intent. Unconcerned with -OkHttp-injected headers like If-None-Match.
    只关注最原始的请求,不去关系请求的资源是否发生了改变,我只关注最后的 response 结果而已。
  • Permitted to short-circuit and not call Chain.proceed().
    因为是第一个被执行的拦截器,因此它有权决定了是否要调用其他拦截,也就是 Chain.proceed() 方法是否要被执行。
  • Permitted to retry and make multiple calls to Chain.proceed()
    因为是第一个被执行的拦截器,因此它有可以多次调用 Chain.proceed() 方法,其实也就是相当与重新请求的作用了。

Network Interceptors

  • Able to operate on intermediate responses like redirects and retries.
    因为 NetworkInterceptor 是排在第 6 个拦截器中,因此可以操作经过 RetryAndFollowup 进行失败重试或者重定向之后得到的resposne。

  • Not invoked for cached responses that short-circuit the network.
    对于从缓存获取的 response 则不会去触发 NetworkInterceptor 。因为响应直接从 CacheInterceptor 返回了。

  • Observe the data just as it will be transmitted over the network.
    观察数据在网络中的传输。

  • Access to the Connection that carries the request.
    可以获得装载请求的连接。

推荐阅读更多精彩内容