本平臺的文章更新會有延遲,你們能夠關注微信公衆號-顧林海,包括年末前會更新kotlin由淺入深系列教程,目前計劃在微信公衆號進行首發,若是你們想獲取最新教程,請關注微信公衆號,謝謝java
前面幾節介紹了OkHttp的同步和異步請求的總體流程以及Dispatcher分發器的做用,接下來介紹一下OkHttp中的五個攔截器。web
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方法中省略了一些代碼,咱們只看核心代碼:cookie
在標記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對象。ide
followUpRequest代碼以下:ui
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是橋接和適配攔截器,它的做用是設置內容長度、編碼方式以及壓縮等等一系列操做,主要是添加頭部的做用。
該攔截器方法以下:
@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。
搜索微信「顧林海」公衆號,按期推送優質文章。