今天項目中遇到需要將從push接收到的數據按照協議parse成應用層需要的結構化數據類型問題;因為push消息類型繁多,等待解析出的結構化數據類型也多樣,有的還需要經過幾步的parse過程;而且因為項目歷史原因,使用Protocal Buffer(push的數據是字節形式的傳遞)。中間嘗試了代理、裝飾等模式,都失敗了,最終還是使用了原始的繼承解決問題;在解決問題個過程中也學習了下OkHttp中的攔截器實現,這里做簡單記錄:
-
攔截器的接口定義如下:
可以看到,在攔截器的回調中,我們可以拿到兩個重要的參數Request和Response,而接口在回調時,會接收一個Chain類型的參數,這個參數保存了Request和Response的相關數據。
-
看一個簡單的Interceptor接口實現例子:
該攔截器實例的功能只是在請求發出前和接收到響應后,分別打印log。Response response=chain.proceed(request); 很重要,它是將攔截連串起來的關鍵。
- 再看下,一個網絡請求的應用吧:
跟進,OkHttpClient.Builder的addInterceptor()方法,可以看到,在OkHttp內部是使用了List保存了添加的所有攔截器。
- 跟進client.newCall(request).execute();的執行過程,查看攔截器的串聯過程:
從上面的方法中可以看到,請求默認會被構造成RealCall類型,再進一步查看RealCall的execute()方法:
上面的紅色方框中這行代碼,它並沒有真正的執行網絡請求,而只是簡單地將請求放入請求池中,讓它等待分派器的后續執行。而真正的執行體在上面的藍色方框中,它封裝了一個攔截器鏈(Chain),並調用了Chain的proced方法,傳入原始的request對象,這里開始攔截器鏈的調用過程:
- 查看proceed方法,可以看到,它使用循環+遞歸的方式,借助函數調用棧,將攔截器串聯起來:
上面的代碼中,真正執行網絡請求的是最后一行綠色方框中的代碼,而當存在多個攔截器時,每個攔截器在執行時都會在上圖的藍色方框中的代碼地方阻塞,等待下一個攔截器的調用返回。
- 為了說明清楚,下面分別以 攔截器鏈中有1個、2個攔截器的場景加以模擬: