OkHttp原始碼分析系列(下)攔截器篇

語言: CN / TW / HK

引言

在上一篇文章中,講了一下OkHttp觸發一個網路請求呼叫順序的一個大概的流程,本篇文章將重點講述一下OkHttp中各個攔截器的作用。篇幅比較長,提前預警一下。 有興趣的可以看一下上一個系列,OkHttp原始碼分析系列(上)流程篇。

概述

先來看一下新增攔截器的位置的程式碼: image.png 可以看到,這裡是一個ArrayList添加了5個具體的攔截器和兩個攔截器List,分別為client.interceptors()和client.networkInterceptors()。接下來將按照新增的順序講一下各個攔截器的作用。

自定義攔截器

首先,在我們的網路請求建立OkHttpClient的時候,呼叫add方法,新增一個自定義的攔截器。下面是新增自定義攔截器的程式碼,及執行後的Log截圖。 image.png image.png 接下來我們看一下自定義攔截器的執行順序,可以看到在getResponseWithInterceptorChain()方法中,最先新增的就是自定義的攔截器,然後我們再Debug看一下這個攔截器的List,可以通過下圖看到,我們在MainActivity中的自定義攔截器,確實是List中的第一個,所以執行的時候也是先執行自定義攔截器,然後再執行OkHttp預設新增的攔截器。 image.png 可以在普通的自定義攔截器中,可以封裝一些公共引數,請求頭,資料加密等操作。

RetryAndFollowUpInterceptor攔截器

重試和重定向攔截器核心程式碼如下: image.png image.png 第一部分其實就是一個攔截器的呼叫,第二部分為重定向的一個方法,這個方法內部其實是一些我們常見的錯誤碼的一些對應處理。 image.png image.png 重定向的最大次數限制為20次,原始碼中是這麼寫的。

Chrome follows 21 redirects; Firefox, curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5.

可以看出這個攔截器的主要功能為:重試和重定向操作。

BridgeInterceptor

BridgeInterceptor,顧名思義,橋樑攔截器。這個橋樑是誰和誰的呢?是應用層和資料層直接的一個攔截器。他的功能主要有兩個,第一個是請求前,第二個是請求後。接下來看一下原始碼: image.png image.png 第一張圖片為:請求前的,可以看到這裡做個了一些 Cookie,User-Agent,Host,Content-Length等的引數設定,這個是所有請求都要設定的一些引數,和HTTP協議有關。 第二張圖片為:通過proceed方法獲取的請求後的引數,對請求後的結果,儲存Cookie和gzip解壓。 這個攔截器的主要功能為:應用層和資料層之間的資料轉換,新增固定的協議引數。

CacheInterceptor

快取攔截器,功能就是讀/寫快取。在請求前獲取是否有快取。請求後快取請求結果。 image.png 快取攔截器觸發的時候,會去判斷一下cache是否為空,這個cache是okhttpclient初始化的時候傳入的,需要注意的是:預設的OkhttpClient是不初始化這個引數的。所以快取攔截器這裡的cache為null。所以CacheStrategy物件的response為null,request不為null。那麼最終還是會去執行的chain.proceed方法。 image.png 如果cache物件不為null會是什麼流程呢? cache物件不為null的時候會執行get方法:cache.get(chain.request()),這個cache內部其實是一個DiskLruCache的get和put操作。需要注意的是cache快取只快取GET請求。具體看如下程式碼。 image.png 除此以外,原始碼中還有這樣的一句註釋,有興趣的同學自行檢視。

Don't cache non-GET responses. We're technically allowed to cache HEAD requests and some POST requests, but the complexity of doing so is high and the benefit is low. 繼續接著講,如果請求的時候對應的request的cache中的response不為null,那麼在請求結束以後,會去取一下伺服器的返回結果碼,如果為304則代表資料沒有修改,則會使用快取結果。 image.png

ConnectInterceptor

連線攔截器乍一看程式碼非常簡單,根本沒幾行程式碼。實際不是這樣的,核心程式碼在下面的紅框中,我們看一下具體內部實現。 image.png 先看一下這個類的構造方法StreamAllocation: image.png 這個類初始化在重試和重定向攔截器,有興趣的同學可以去翻原始碼看一下,這幾個引數都是在那邊設定的。這裡具體講一下第一個連線池。連線池是在建立client物件的時候初始化了一個預設的連線池。 image.png 最大閒置連線數量為5個,最大時間閒置時間為5分鐘。 這個連線池內部有一個RealConnection的雙端佇列,connectionpool的put/get操作,都是針對這個佇列的操作。 這個類基本就是連線攔截器的核心了,比較關鍵。我們繼續往下看。 image.png RealConnection實現了Connection介面,看一下這個介面的程式碼。Route,Socket,Handshake,Protocol。 這不就是路由,Socket連線,握手,協議介面。 image.png 接下來我們回到攔截器這裡,繼續看StreamAllocation,呼叫了他的newStream方法,我們看一下。 image.png 繼續看一下findHealthyConnection方法: image.png 這裡是一個while true的迴圈方法,繼續往下看。這個方法的程式碼比較多,我就不貼出來了,可以看一下核心程式碼: image.png image.png 這些方法都是在RealConnection裡面的,主要是發起Socket請求和協議相關。 最後需要注意的是連線聯結器呼叫的proceed方法和其他攔截器的呼叫方法都不一樣。其他的是呼叫的一個引數的方法,這裡呼叫的是4個引數的。 image.png

CallServerInterceptor

最後一個攔截器了。首先看到一行註釋:

This is the last interceptor in the chain. It makes a network call to the server

這基本就概述了這個攔截器的作用了。主要通過HttpCodec介面,根據Http協議的版本有對應的codec,由對應的codec來實現請求結果的獲取。生成最後的Response。 image.png

結束語

OkHttp兩篇文章寫完,就基本講的差不多了。原始碼看著也比較複雜頭痛,看著看著就不知道看到哪裡去了。。下個系列還在準備中,下篇文章再見。

轉載請註明出處,微信公眾號的轉載請聯絡本人授權。