Okhttp之 拦截器(Interceptor)专题

引子:

对于Okhttp的使用,不能仅限于“会”用,而是要了解其原理。在尝试了解原理的过程中,查到 拦截器的概念。

拦截器是OkHttp 执行网络请求中的重要角色,贯穿了整个请求执行的过程。(注:okhttp2.2以后才有拦截器的概念,2.2以后经过了一次代码重构,加入了拦截器机制)

为了了解拦截器,阅读 官方文档是必不可少的步骤,地址如下:https://github.com/square/okhttp/wiki/Interceptors 

本文主要将官方文档中的重要概念,以及重要段落的翻译一一写明,可能有翻译的不周到的地方,见谅。

 

重要概念

1)拦截器的地位

下图是okhttp工作流程图

 

可以看出,Interceptor贯穿了整个请求过程,是在请求执行过程中扮演重要角色。

这是okhttp的请求执行过程,从应用发出request,到应用收到response,期间经历了N个拦截器。

2)拦截器的作用

来自官网的英文原文:

Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls. 

意思大概是,拦截器是一个强有力的机制,能够监控,重写以及重试(请求的)调用。

 以下是官网提供的一个Interceptor例子,只用来打印拦截到的Response的相关日志:

 1 class LoggingInterceptor implements Interceptor {
 2     String tag = "MainActivityXXXX";
 3 
 4     @Override
 5     public Response intercept(Interceptor.Chain chain) throws IOException {
 6         Request request = chain.request();
 7 
 8         long t1 = System.nanoTime();
 9         Log.d(tag, String.format("Sending request %s on %s%n%s",
10                 request.url(), chain.connection(), request.headers()));
11 
12         Response response = chain.proceed(request);
13 
14         long t2 = System.nanoTime();
15         Log.d(tag, String.format("Received response for %s in %.1fms%n%s",
16                 response.request().url(), (t2 - t1) / 1e6d, response.headers()));
17 
18         return response;
19     }
20 }

只是一个Interceptor太抽象,下面我通过一个具体的Activity展示拦截器的用法:

 1 package com.test.hank.test2;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.util.Log;
 6 
 7 import java.io.IOException;
 8 
 9 import okhttp3.Call;
10 import okhttp3.Callback;
11 import okhttp3.Interceptor;
12 import okhttp3.OkHttpClient;
13 import okhttp3.Request;
14 import okhttp3.Response;
15 
16 public class Main2Activity extends Activity {
17 
18     @Override
19     protected void onCreate(Bundle savedInstanceState) {
20         super.onCreate(savedInstanceState);
21         setContentView(R.layout.activity_main2);
22         testAsyncGetRequest();
23     }
24 
25     private static final String tag = "main2tag";
26     private static final String url = "http://www.publicobject.com/helloworld.txt";
27 
28     //这是一个普通的okhttp异步get请求
29     private void testAsyncGetRequest() {
30         OkHttpClient okHttpClient = new OkHttpClient().newBuilder().addInterceptor(new LoggingInterceptor()).build();//新建一个okhttpClient对象,并且设置拦截器
31         Request request = new Request.Builder().url(url).build();//新建Request对象
32         Callback callback = new Callback() {// 新建异步请求的回调函数
33             @Override
34             public void onFailure(Call call, IOException e) {
35                 Log.d(tag, "request Failed ; " + e.getLocalizedMessage());
36             }
37 
38             @Override
39             public void onResponse(Call call, Response response) throws IOException {
40                 if (response.isSuccessful()) {
41                     Log.d(tag, "onResponse:" + response.body().string());
42                 } else {
43                     Log.d(tag, "onResponse failed");
44                 }
45             }
46         };
47         okHttpClient.newCall(request).enqueue(callback);//用okhttpClient执行request,并且注册回调函数
48 
49     }
50 
51 
52     /**
53      *  这是按照官方的示例Interceptor改的,打印日志的方式换成了Log.d().
54      */
55     class LoggingInterceptor implements Interceptor {
56 
57         @Override
58         public Response intercept(Chain chain) throws IOException {
59             //第一步,获得chain内的request
60             Request request = chain.request();
61             Log.d(tag, "intercept-request:" + request.url());
62             //第二步,用chain执行request
63             Response response = chain.proceed(request);
64             Log.d(tag, "intercept-response" + "-" + response.request().url());
65             //第三步,返回response
66             return response;
67         }
68     }
69 }

对照最上方的Okhttp工作流程图,可以观察到,在OkhttpCore即将把response返回给application时,拦截器率先得到了response对象。而在上方的代码中,只是对request和response进行了日志打印,并没有实际操作。

但是事实上,拦截器拿到了request之后,可以对request进行重写,可以添加,移除,替换请求头,也能对response的header进行重写,改变response的body。佐证如下,文档英文原文:

Rewriting Requests

Interceptors can add, remove, or replace request headers. They can also transform the body of those requests that have one. For example, you can use an application interceptor to add request body compression if you're connecting to a webserver known to support it.
Rewriting Responses

Symmetrically, interceptors can rewrite response headers and transform the response body. This is generally more dangerous than rewriting request headers because it may violate the webserver's expectations!

上面的Activity运行起来之后,可以在日志中看到如下内容:证明,拦截器确实可以获得response。

04-03 22:54:17.918 11961-12042/com.test.hank.test2 D/main2tag: intercept-request:http://www.publicobject.com/helloworld.txt
04-03 22:54:21.134 11961-12042/com.test.hank.test2 D/main2tag: intercept-response-https://publicobject.com/helloworld.txt
04-03 22:54:21.142 11961-12042/com.test.hank.test2 D/main2tag: onResponse:
                             \           //
                              \  .ooo.  //
                               .@@@@@@@@@.
                             :@@@@@@@@@@@@@:
                            :@@. '@@@@@' .@@:
                            @@@@@@@@@@@@@@@@@
                            @@@@@@@@@@@@@@@@@
.......

  综上所述,拦截器的作用就是:可以在应用拿到response之前,先获得response,对其中某些数据进行监控,在有必要的情况下,对response的某些内容(比如response的header,body,response内的request的header,body)进行更改。

 (至于太高深的拦截器用法,笔者能力有限,只能研究到这里,有缘分的话,再深入研究)。

 3 拦截器的分类

  okhttp工作流程图中,橙色框框内的那些拦截器,属于okhttp库内部定义的,一般情况下不会更改。所以这里只讨论开发者能够自定义的拦截器。

  分为两类:

  1)ApplicationInterceptor(应用拦截器)

  2)NetworkInterceptor(网络拦截器)

  他们的异同:

  相同点,

  1)都能对server返回的response进行拦截(好像是废话···)

       2)这两种拦截器本质上都是基于Interceptor接口,由开发者实现这个接口,然后将自定义的Interceptor类的对象设置到okhttpClient对象中(参见上面的Main2Activity代码)。所以,他们的对象,本质上没什么不同,都是Interceptor的实现类的对象。

  3)两者都会被add到OkHttpClient内的一个ArrayList中。当请求执行的时候,多个拦截器会依次执行(list本身就是有序的)。

  不同点,

   1)okhttpClient添加两种拦截器的api不同。添加应用拦截器的接口是addInterceptor(),而添加网络拦截器的接口是addNetworkInterceptor().

   2)两者负责的区域不同,从最上方图中可以看出,应用拦截器作用于okhttpCore到Application之间,网络拦截器作用于 network和okhttpCore之间

   3)在某种特殊情况下(比如:访问了一个url,结果这个url发生了重定向),网络拦截器有可能被执行多次,但是 不论任何情况,application只会被执行一次。(这个,证明起来十分简单,只需要将上面代码中30行的addInterceptor改成addNetworkInterceptor,运行起来再观察日志打印,就会发现,内容被打印了两次,我就不再试了,有兴趣的可自己运行代码)

  其实官方文档中,对两种拦截器的异同做了更标准的说明,但是我翻译了一下觉得一脸懵逼,所以就自己总结了以上的几点异同,但是官方的说明我还是贴在下面。

Choosing between application and network interceptors

Each interceptor chain has relative merits.

Application interceptors

Don't need to worry about intermediate responses like redirects and retries.
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.
Permitted to short-circuit and not call Chain.proceed().
Permitted to retry and make multiple calls to Chain.proceed().

Network Interceptors Able to operate on intermediate responses like redirects and retries. Not invoked for cached responses that short-circuit the network. Observe the data just as it will be transmitted over the network. Access to the Connection that carries the request.

  就说到这里吧,以上内容都是自己根据官方文档加上自己做实验得出的确切的结论。能写出来的都是自己的浅显理解,不求装B,只求能说明白,让人读起来畅通,好理解。

  下一篇准备研究一下,okhttp对于https请求的处理。

原文地址:https://www.cnblogs.com/hankzhouAndroid/p/8710284.html