引子:
對於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請求的處理。