本平臺的文章更新會有延遲,大家可以關注微信公眾號-顧林海,包括年底前會更新kotlin由淺入深系列教程,目前計劃在微信公眾號進行首發,如果大家想獲取最新教程,請關注微信公眾號,謝謝
前面幾節介紹了OkHttp的同步和非同步請求的整體流程以及Dispatcher分發器的作用,接下來介紹一下OkHttp中的五個攔截器。
RetryAndFollowUpInterceptor攔截器
RetryAndFollowupInterceptor是重試重定向攔截器,它的主要作用是負責失敗重連。OkHttp中的重定向功能是預設開啟的。
該攔截器方法如下:
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
...
//標記1
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
...
while (true) {
//取消,釋放
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
...
//標記2
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
...
Request followUp;
//標記3
followUp = followUpRequest(response, streamAllocation.route());
...
//標記4
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
...
}
}
複製程式碼
在intercept方法中省略了一些程式碼,我們只看核心程式碼:
在標記1處建立了StreamAllocation物件,建立的StreamAllocation物件在RetryAndFollowupInterceptor攔截器中並沒有使用到。
StreamAllocation物件會通過標記2處的proceed方法傳遞給下一個攔截器,直到ConnectInterceptor攔截器,StreamAllocation的主要作用是提供建立HTTP請求所需要的網路元件,ConnectInterceptor從RealInterceptorChain獲取前面的Interceptor傳過來的StreamAllocation物件,執行StreamAllocation物件的newStream()方法完成所有的連線建立工作,並將這個過程中建立的用於網路IO的RealConnection物件,以及與伺服器互動最為關鍵的HttpCodec等物件傳遞給後面的CallServerInterceptor攔截器。
在標記3處,會對返回的Response進行檢查,通過followUpRequest方法對Response返回的狀態碼判斷,並建立重定向需要發出的Request物件。
followUpRequest程式碼如下:
private Request followUpRequest(Response userResponse, Route route) throws IOException {
if (userResponse == null) throw new IllegalStateException();
int responseCode = userResponse.code();
final String method = userResponse.request().method();
switch (responseCode) {
case HTTP_PROXY_AUTH://407 代理伺服器認證
...
case HTTP_UNAUTHORIZED://401 身份未認證
...
case HTTP_PERM_REDIRECT://308
case HTTP_TEMP_REDIRECT://307
//當請求的method不為GET和HEAD時不進行重定向
...
case HTTP_MULT_CHOICE://300 多種選擇
case HTTP_MOVED_PERM://301 永久移除
case HTTP_MOVED_TEMP://302 臨時重定向
case HTTP_SEE_OTHER://303 其他問題
...
case HTTP_CLIENT_TIMEOUT://408 請求超時
...
case HTTP_UNAVAILABLE://503 服務不可用
...
default:
return null;
}
}
複製程式碼
followUpRequest方法主要是對返回的狀態碼進行判斷,根據特定的狀態碼建立重定向需要的Request物件。
回到上面攔截器intercept方法,在標記4判斷followUpCount是否大於MAX_FOLLOW_UPS(20),也就是說重定向次數上限為20,當重試次數超過20的時候,會釋放StreamAllocation這個物件,這樣做是為了防止無限制的重試網路請求,從而造成資源的浪費,關於重定向的次數建議可以按照Chrome遵循21個重定向;Firefox、CURL和WGET遵循20;Safari遵循16;HTTP/1推薦5。
總結RetryAndFollowupInterceptor攔截器:
-
建立StreamAllocation物件。
-
呼叫RealInterceptorChain.proceed()進行網路請求。
-
根據異常結果或響應結果判斷是否進行重新請求。
-
呼叫下一個攔截器,對Response進行處理,返回給上一個攔截器。
BridgeInterceptor攔截器
BridgeInterceptor是橋接和適配攔截器,它的作用是設定內容長度、編碼方式以及壓縮等等一系列操作,主要是新增頭部的作用。
該攔截器方法如下:
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
//====================新增頭部資訊========================
RequestBody body = userRequest.body();
...
if (userRequest.header("Connection") == null) {
//標記1
requestBuilder.header("Connection", "Keep-Alive");
}
...
//標記2
Response networkResponse = chain.proceed(requestBuilder.build());
//標記3
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
//標記4
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
//標記5
GzipSource responseBody = new GzipSource(networkResponse.body().source());
...
}
return responseBuilder.build();
}
複製程式碼
intercept方法主要是給Request新增一些頭部資訊,在標記1處的Connection設定為Keep-Alive,Keep-Alive的作用是當我們開啟一個HTTP連線後,在一定時間內保持它的連線狀態。
在標記2處呼叫了攔截器鏈的proceed方法向伺服器發起請求。
在標記3處通過HttpHeaders的receiveHeaders方法,通過網路請求,伺服器返回的Response轉化為客戶端可以識別的Response,如果HTTP預設支援gzip,那麼BridgeInterceptor攔截器將會對這個Response進行解壓,最終得到客戶端使用的Response。
在標記4處,對transparentGzip標誌位進行判斷,如果transparentGzip為true就表面Accept-Encoding支援gzip壓縮;再判斷頭部的Content-Encoding是否為gzip,保證服務端返回的響應體內容是經過了gzip壓縮的;最後判斷HTTP頭部是否有Body。當滿足這些條件後在標記5處將Response的body轉換為GzipSource型別,這樣的話client端直接以解壓的方式讀取資料。
總結BridgeInterceptor攔截器:
-
負責將使用者構建的一個Request請求轉化為能夠進行網路訪問的請求,通過給頭部新增資訊。
-
將這個符合網路請求的Request進行網路請求。
-
將網路請求回來的響應Response轉化為使用者可用的Response。
搜尋微信“顧林海”公眾號,定期推送優質文章。