1、攔截器 Interceptor
1.1 okhttp的工作流程圖

可以看出,Interceptor貫穿了整個(gè)請求過程,是在請求執(zhí)行過程中扮演重要角色。
這是okhttp的請求執(zhí)行過程,從應(yīng)用發(fā)出request,到應(yīng)用收到response,期間經(jīng)歷了N個(gè)攔截器。
- 藍(lán)色塊上方是APPLication Interceptor,也就是應(yīng)用程序攔截器,即開發(fā)者自己自定義的攔截器,代碼中的client.interceptors()獲取的就是這類攔截器
- 藍(lán)色塊下方是NetWork Interceptor,也就是網(wǎng)絡(luò)攔截器,用來觀察單個(gè)網(wǎng)絡(luò)請求和響應(yīng),只能調(diào)用一次proceed方法
- 藍(lán)色塊代表的就是OKHttp提供的攔截器,共有5個(gè),也是我們需要關(guān)注的重點(diǎn)
1.2 攔截器的分類
okhttp工作流程圖中,橙色框框內(nèi)的那些攔截器,屬于okhttp庫內(nèi)部定義的,一般情況下不會更改。所以這里只討論開發(fā)者能夠自定義的攔截器。
分為兩類:
1)ApplicationInterceptor(應(yīng)用攔截器)
2)NetworkInterceptor(網(wǎng)絡(luò)攔截器)
相同點(diǎn)
- 都能對server返回的response進(jìn)行攔截
- 這兩種攔截器本質(zhì)上都是基于Interceptor接口,由開發(fā)者實(shí)現(xiàn)這個(gè)接口,然后將自定義的Interceptor類的對象設(shè)置到okhttpClient對象中。所以,他們的對象,本質(zhì)上沒什么不同,都是Interceptor的實(shí)現(xiàn)類的對象。
- 兩者都會被add到OkHttpClient內(nèi)的一個(gè)ArrayList中。當(dāng)請求執(zhí)行的時(shí)候,多個(gè)攔截器會依次執(zhí)行(list本身就是有序的)。
不同點(diǎn)
- okhttpClient添加兩種攔截器的api不同。添加應(yīng)用攔截器的接口是addInterceptor(),而添加網(wǎng)絡(luò)攔截器的接口是addNetworkInterceptor().
- 兩者負(fù)責(zé)的區(qū)域不同,從最上方圖中可以看出,應(yīng)用攔截器作用于okhttpCore到Application之間,網(wǎng)絡(luò)攔截器作用于 network和okhttpCore之間
- 在某種特殊情況下(比如:訪問了一個(gè)url,結(jié)果這個(gè)url發(fā)生了重定向),網(wǎng)絡(luò)攔截器有可能被執(zhí)行多次,但是不論任何情況,application只會被執(zhí)行一次。
- 在執(zhí)行時(shí)addInterceptor一定會執(zhí)行,addNetworkInterceptor不一定會執(zhí)行,因?yàn)樵赼ddNetworkInterceptor之前的Interceptor發(fā)生異常退出時(shí)addNetworkInterceptor就不會被執(zhí)行。
- addInterceptor 先拿到的是Request后拿到的是Response,addNetworkInterceptor反之。
- 打印Log日志時(shí),一把放在 addNetworkInterceptor,因?yàn)槠浯蛴〉氖峭暾?Request。
1.3 okhttp庫內(nèi)部定義的攔截器
1.3.0、RealCall
在okhttp框架中,當(dāng)客戶端通過OkHttpClient發(fā)起同步或異步請求時(shí),okhttp框架將會創(chuàng)建一個(gè)RealCall,這個(gè)實(shí)例將根據(jù)客戶端提供的Request,發(fā)起同步或異步網(wǎng)絡(luò)請求操作,在RealCall被創(chuàng)建時(shí),將會創(chuàng)建一個(gè)Interceptor的具體實(shí)現(xiàn)。我們知道,okhttp框架將網(wǎng)絡(luò)請求的步驟,通過Interceptor接口進(jìn)行了統(tǒng)一的分層式設(shè)計(jì),將每個(gè)環(huán)節(jié)都分成了不同的Interceptor,Interceptor又被稱為攔截器,這是該網(wǎng)絡(luò)框架設(shè)計(jì)的精髓所在,通過不同的攔截器規(guī)則,處理網(wǎng)絡(luò)請求過程中的不同環(huán)節(jié),最終通過鏈?zhǔn)秸{(diào)用,實(shí)現(xiàn)一個(gè)完整的網(wǎng)絡(luò)請求操作。
Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList<>();
//添加開發(fā)者應(yīng)用層自定義的Interceptor
interceptors.addAll(client.interceptors());
//這個(gè)Interceptor是處理請求失敗的重試,重定向
interceptors.add(retryAndFollowUpInterceptor);
//這個(gè)Interceptor工作是添加一些請求的頭部或其他信息
//并對返回的Response做一些友好的處理(有一些信息你可能并不需要)
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//這個(gè)Interceptor的職責(zé)是判斷緩存是否存在,讀取緩存,更新緩存等等
interceptors.add(new CacheInterceptor(client.internalCache()));
//這個(gè)Interceptor的職責(zé)是建立客戶端和服務(wù)器的連接
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//添加開發(fā)者自定義的網(wǎng)絡(luò)層攔截器
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
//一個(gè)包裹這request的chain
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
//把chain傳遞到第一個(gè)Interceptor手中
return chain.proceed(originalRequest);
}

1.3.1、RetryAndFollowUpInterceptor 重試和失敗重定向攔截器
這個(gè)攔截器它的作用主要是負(fù)責(zé)請求的重定向操作,用于處理網(wǎng)絡(luò)請求中,請求失敗后的重試鏈接。把StreamAllocation對象,傳遞給后面的攔截器。
private static final int MAX_FOLLOW_UPS = 20;
從這個(gè)靜態(tài)變量可以看出 RetryAndFollowUpInterceptor 重定向最多20次。
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//創(chuàng)建一個(gè)新的流
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
//重定向次數(shù)
int followUpCount = 0;
// 上一個(gè)重試得到的響應(yīng)
Response priorResponse = null;
while (true) {
//如果RealCall調(diào)用了cancel,即取消請求,那就釋放資源,拋出異常結(jié)束請求
if (canceled) {
//如果取消了則刪除連接上的call請求
streamAllocation.release();
throw new IOException("Canceled");
}
// 定義請求的響應(yīng)
Response response = null;
//// 是否釋放連接,默認(rèn)為true
boolean releaseConnection = true;
try {
//調(diào)用下一個(gè)攔截器 即BridgeInterceptor;進(jìn)行網(wǎng)絡(luò)連接,獲取response
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
// 如果沒有發(fā)送異常,修改標(biāo)志 不需要重試
releaseConnection = false;
} catch (RouteException e) {
//出現(xiàn)路由連接異常,通過recover方法判斷能否恢復(fù)連接,如果不能將拋出異常不再重試
//recover(...)檢測連接是否還可以繼續(xù)
if (!recover(e.getLastConnectException(), false, request)) {
throw e.getLastConnectException();
}
//能恢復(fù)連接,修改標(biāo)志 不釋放連接
releaseConnection = false;
//回到下一次循環(huán) 繼續(xù)重試 除了finally代碼外,下面的代碼都不會執(zhí)行
continue;
} catch (IOException e) {//后續(xù)攔截器在與服務(wù)器通信中拋出IO異常
//判斷該異常是否是連接關(guān)閉異常
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
////通過recover方法判斷能否恢復(fù)連接,如果不能將拋出異常不再重試
if (!recover(e, requestSendStarted, request)) throw e;
//能恢復(fù)連接, 修改標(biāo)志 不釋放連接
releaseConnection = false;
//回到下一次循環(huán) 繼續(xù)重試 除了finally代碼外,下面的代碼都不會執(zhí)行
continue;
} finally {
// 如果releaseConnection為true,說明后續(xù)攔截器拋出了其它異常,那就釋放所有資源,結(jié)束請求
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// 走到這里,說明網(wǎng)絡(luò)請求已經(jīng)完成了,但是響應(yīng)碼并不一定是200
// 可能是其它異常的響應(yīng)碼或者重定向響應(yīng)碼
// 如果priorResponse 不等于null,說明前面已經(jīng)完成了一次請求
// 那就通過上一次的response構(gòu)建新的response,但是body為null.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
//對response進(jìn)行響應(yīng)碼的判斷,如果需要進(jìn)行重定向,那就獲取新的Request
Request followUp = followUpRequest(response);
// 如果為null,那就沒必要重新請求,說明已經(jīng)有了合適的Response,直接返回
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
//關(guān)閉,忽略任何已檢查的異常
closeQuietly(response.body());
//檢測followUp(重定向)次數(shù)是否超過20次,如果超過就拋出異常,避免消耗客戶端太多資源
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
//如果該請求體被UnrepeatableRequestBody標(biāo)記,則不可重試
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
//判斷重連前的Request與重新構(gòu)建的Request是否有相同的連接,即host、port、scheme是否一致
if (!sameConnection(response, followUp.url())) {
// 如果不是相同的url連接,先釋放之間的,再創(chuàng)建新的StreamAllocation
streamAllocation.release();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(followUp.url()), callStackTrace);
} else if (streamAllocation.codec() != null) {
// 如果相同,但是本次請求的流沒有關(guān)閉,那就拋出異常
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
//把重定向的請求賦值給request,以便再次進(jìn)入循環(huán)執(zhí)行
request = followUp;
priorResponse = response;
}
}
RetryAndFollowUpInterceptor ,主要就是完成兩件事情:重試與重定向。
-
重試
重試流程
請求階段發(fā)生了 RouteException 或者 IOException會進(jìn)行判斷是否重新發(fā)起請求。
RouteException
catch (RouteException e) {
// 路由異常,連接未成功,請求還沒發(fā)出去
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
}
IOException
catch (IOException e) {
// 請求發(fā)出去了,但是和服務(wù)器通信失敗了。(socket流正在讀寫數(shù)據(jù)的時(shí)候斷開連接)
//HTTP2才會拋出ConnectionShutdownException。所以對于HTTP1 requestSendStarted一定是true
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
}
兩個(gè)異常都是根據(jù) recover 方法判斷是否能夠進(jìn)行重試,如果返回 true ,則表示允許重試。
/**
* 判斷當(dāng)與服務(wù)器通信失敗時(shí),連接能否進(jìn)行恢復(fù)
* 返回true,表示可以進(jìn)行恢復(fù)
* 返回false 表示不能恢復(fù),即不能重連
*/
private boolean recover(IOException e, boolean requestSendStarted, Request userRequest) {
//根據(jù)拋出的異常,做出連接、連接路線的一些處理,并且釋放連接,關(guān)閉連接
streamAllocation.streamFailed(e);
// 判斷開發(fā)者是否禁用了失敗重連
// 在構(gòu)建OKHttpClient的時(shí)候可以通過build進(jìn)行配置
//如果禁用,那就返回false,不進(jìn)行重連
if (!client.retryOnConnectionFailure()) return false;
// 如果不是連接關(guān)閉異常,且請求體被UnrepeatableRequestBody標(biāo)記,那不能恢復(fù)
//如果是IOException,由于requestSendStarted只在http2的io異常中可能為false
if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) return false;
// 判斷是不是屬于重試的異常
if (!isRecoverable(e, requestSendStarted)) return false;
// 有沒有可以用來連接的路由路線
// 如果沒有,返回false
if (!streamAllocation.hasMoreRoutes()) return false;
// 走到這里說明可以恢復(fù)連接,嘗試重連
return true;
}
所以首先使用者在不禁止重試的前提下,如果出現(xiàn)了某些異常,并且存在更多的路由線路,則會嘗試換條線路進(jìn)行請求的重試。其中某些異常是在 isRecoverable 中進(jìn)行判斷:
private boolean isRecoverable(IOException e, boolean requestSendStarted) {
// 出現(xiàn)協(xié)議異常,不能重試
if (e instanceof ProtocolException) {
return false;
}
// 如果不是超時(shí)異常,不能重試
if (e instanceof InterruptedIOException) {
return e instanceof SocketTimeoutException && !requestSendStarted;
}
//SSL握手異常中,證書出現(xiàn)問題,不能重試
if (e instanceof SSLHandshakeException) {
if (e.getCause() instanceof CertificateException) {
return false;
}
}
// SSL握手未授權(quán)異常 不能重試
if (e instanceof SSLPeerUnverifiedException) {
return false;
}
return true;
}
- 1、
協(xié)議異常:如果是那么直接判定不能重試;(你的請求或者服務(wù)器的響應(yīng)本身就存在問題,沒有按照http協(xié)議來定義數(shù)據(jù),再重試也沒用)- 2、
超時(shí)異常:可能由于網(wǎng)絡(luò)波動造成了Socket連接的超時(shí),可以使用不同路線重試。- 3、
SSL證書異常/SSL驗(yàn)證失敗異常:前者是證書驗(yàn)證失敗,后者可能就是壓根就沒證書,或者證書數(shù)據(jù)不正確,那還怎么重試?經(jīng)過了異常的判定之后,如果仍然允許進(jìn)行重試,就會再檢查當(dāng)前有沒有可用路由路線來進(jìn)行連接。簡單來說,比如 DNS 對域名解析后可能會返回多個(gè) IP,在一個(gè)IP失敗后,嘗試另一個(gè)IP進(jìn)行重試。
-
重定向
如果請求結(jié)束后沒有發(fā)生異常并不代表當(dāng)前獲得的響應(yīng)就是最終需要交給用戶的,還需要進(jìn)一步來判斷是否需要重定向的判斷。重定向的判斷位于 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) {
//407 客戶端使用了HTTP代理服務(wù)器,在請求頭中添加 “Proxy-Authorization”,讓代理服務(wù)器授權(quán)
case HTTP_PROXY_AUTH:
Proxy selectedProxy = route != null
? route.proxy()
: client.proxy();
if (selectedProxy.type() != Proxy.Type.HTTP) {
throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
}
return client.proxyAuthenticator().authenticate(route, userResponse);
// 401 需要身份驗(yàn)證 有些服務(wù)器接口需要驗(yàn)證使用者身份 在請求頭中添加 “Authorization”
case HTTP_UNAUTHORIZED:
return client.authenticator().authenticate(route, userResponse);
// 308 永久重定向
// 307 臨時(shí)重定向
case HTTP_PERM_REDIRECT:
case HTTP_TEMP_REDIRECT:
// "If the 307 or 308 status code is received in response to a request other than GET
// 如果請求方式不是GET或者HEAD,框架不會自動重定向請求
if (!method.equals("GET") && !method.equals("HEAD")) {
return null;
}
// 300 301 302 303
case HTTP_MULT_CHOICE:
case HTTP_MOVED_PERM:
case HTTP_MOVED_TEMP:
case HTTP_SEE_OTHER:
// 如果用戶不允許重定向,那就返回null
if (!client.followRedirects()) return null;
//從響應(yīng)頭取出location
String location = userResponse.header("Location");
if (location == null) return null;
//根據(jù)location 配置新的請求 url
HttpUrl url = userResponse.request().url().resolve(location);
// 如果為null,說明協(xié)議有問題,取不出來HttpUrl,那就返回null,不進(jìn)行重定向
if (url == null) return null;
// 如果重定向在http到https之間切換,需要檢查用戶是不是允許(默認(rèn)允許)
boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());
if (!sameScheme && !client.followSslRedirects()) return null;
// Most redirects don't include a request body.
Request.Builder requestBuilder = userResponse.request().newBuilder();
/**
* 重定向請求中 只要不是 PROPFIND 請求,無論是POST還是其他的方法都要改為GET請求方式,
* 即只有 PROPFIND 請求才能有請求體
*/
//請求不是get與head
if (HttpMethod.permitsRequestBody(method)) {
final boolean maintainBody = HttpMethod.redirectsWithBody(method);
// 除了 PROPFIND 請求之外都改成GET請求
if (HttpMethod.redirectsToGet(method)) {
requestBuilder.method("GET", null);
} else {
RequestBody requestBody = maintainBody ? userResponse.request().body() : null;
requestBuilder.method(method, requestBody);
}
//不是 PROPFIND 的請求,把請求頭中關(guān)于請求體的數(shù)據(jù)刪掉
if (!maintainBody) {
requestBuilder.removeHeader("Transfer-Encoding");
requestBuilder.removeHeader("Content-Length");
requestBuilder.removeHeader("Content-Type");
}
}
// 在跨主機(jī)重定向時(shí),刪除身份驗(yàn)證請求頭
if (!sameConnection(userResponse, url)) {
requestBuilder.removeHeader("Authorization");
}
return requestBuilder.url(url).build();
//408 客戶端請求超時(shí)
case HTTP_CLIENT_TIMEOUT:
//408 算是連接失敗了,所以判斷用戶是不是允許重試
if (!client.retryOnConnectionFailure()) {
// The application layer has directed us not to retry the request.
return null;
}
// UnrepeatableRequestBody實(shí)際并沒發(fā)現(xiàn)有其他地方用到
if (userResponse.request().body() instanceof UnrepeatableRequestBody) {
return null;
}
// 如果是本身這次的響應(yīng)就是重新請求的產(chǎn)物同時(shí)上一次之所以重請求還是因?yàn)?08,那我們這次不再重請求了
if (userResponse.priorResponse() != null
&& userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {
// We attempted to retry and got another timeout. Give up.
return null;
}
//如果服務(wù)器告訴我們了 Retry-After 多久后重試,那框架不管了。
if (retryAfter(userResponse, 0) > 0) {
return null;
}
return userResponse.request();
//503 服務(wù)不可用 和408差不多,但是只在服務(wù)器告訴你 Retry-After:0(意思就是立即重試) 才重請求
case HTTP_UNAVAILABLE:
if (userResponse.priorResponse() != null
&& userResponse.priorResponse().code() == HTTP_UNAVAILABLE) {
// We attempted to retry and got another timeout. Give up.
return null;
}
if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
// specifically received an instruction to retry without delay
return userResponse.request();
}
return null;
default:
return null;
}
}
整個(gè)是否需要重定向的判斷內(nèi)容很多,記不住,這很正常,關(guān)鍵在于理解他們的意思。如果此方法返回空,那就表示不需要再重定向了,直接返回響應(yīng);但是如果返回非空,那就要重新請求返回的 Request ,但是需要注意的是,我們的 followup 在攔截器中定義的最大次數(shù)為20次。
總結(jié)
本攔截器是整個(gè)責(zé)任鏈中的第一個(gè),這意味著它會是首次接觸到 Request 與最后接收到 Response 的角色,在這個(gè)攔截器中主要功能就是判斷是否需要重試與重定向。
重試的前提是出現(xiàn)了 RouteException 或者 IOException 。一但在后續(xù)的攔截器執(zhí)行過程中出現(xiàn)這兩個(gè)異常,就會通過 recover 方法進(jìn)行判斷是否進(jìn)行連接重試。
重定向發(fā)生在重試的判定之后,如果不滿足重試的條件,還需要進(jìn)一步調(diào)用 followUpRequest 根據(jù) Response 的響應(yīng)碼(當(dāng)然,如果直接請求失敗, Response 都不存在就會拋出異常)。 followup 最大發(fā)生20次。
1.3.2、BridgeInterceptor 橋接攔截器
主要是補(bǔ)充用戶創(chuàng)建請求當(dāng)中缺少的一些必要的請求頭。BridgeInterceptor 為用戶構(gòu)建的一個(gè) Request 請求轉(zhuǎn)化為能夠進(jìn)行網(wǎng)絡(luò)訪問的請求,同時(shí)將網(wǎng)絡(luò)請求回來的響應(yīng) Response 轉(zhuǎn)化為用戶可用的 Response。比如設(shè)置請求內(nèi)容長度,編碼,gzip壓縮,cookie等,獲取響應(yīng)后保存Cookie等操作。這個(gè)攔截器相對比較簡單。

得到響應(yīng):
- 1、讀取Set-Cookie響應(yīng)頭并調(diào)用接口告知用戶,在下次請求則會讀取對應(yīng)的數(shù)據(jù)設(shè)置進(jìn)入請求頭, 默認(rèn)CookieJar無實(shí)現(xiàn);
- 2、響應(yīng)頭Content-Encoding為gzip,使用GzipSource包裝解析。
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
//組織Request Header包括這是keep-alive, Cookie添加,gzip等
....
//傳遞
Response networkResponse = chain.proceed(requestBuilder.build());
//組織Response Header 包括cookie保存更新,Gzip解壓等
....
return responseBuilder.build();
}
請求頭
- 1、
Content-Type:請求體類型,如: application/x-www-form-urlencoded- 2、
Content-Length / Transfer-Encoding:請求體解析方式- 3、
Host:請求的主機(jī)站點(diǎn)- 4、
Connection: Keep-Alive:保持長連接- 5、
Accept-Encoding: gzip:接受響應(yīng)支持gzip壓縮- 6、
Cookie:cookie身份辨別- 7、
User-Agen:請求的用戶信息,如:操作系統(tǒng)、瀏覽器等
在補(bǔ)全了請求頭后交給下一個(gè)攔截器處理,得到響應(yīng)后,主要干兩件事情
- 1、保存cookie,在下次請求則會讀取對應(yīng)的數(shù)據(jù)設(shè)置進(jìn)入請求頭,默認(rèn)的 CookieJar 不提供實(shí)現(xiàn)
- 2、如果使用gzip返回的數(shù)據(jù),則使用 GzipSource 包裝便于解析。
總結(jié)
橋接攔截器的執(zhí)行邏輯主要就是以下幾點(diǎn):
對用戶構(gòu)建的 Request 進(jìn)行添加或者刪除相關(guān)頭部信息,以轉(zhuǎn)化成能夠真正進(jìn)行網(wǎng)絡(luò)請求的 Request 將符合網(wǎng)絡(luò)請求規(guī)范的Request交給下一個(gè)攔截器處理,并獲取 Response 如果響應(yīng)體經(jīng)過了GZIP壓縮,那就需要解壓,再構(gòu)建成用戶可用的 Response 并返回。
1.3.3、CacheInterceptor 緩存攔截器
如果當(dāng)前未使用網(wǎng)絡(luò),并且緩存不可以使用,通過構(gòu)建者模式創(chuàng)建一個(gè)Response響應(yīng),拋出504錯(cuò)誤。如果有緩存 但是不能使用網(wǎng)絡(luò) ,直接返回緩存結(jié)果。這是在進(jìn)行網(wǎng)絡(luò)請求之前所做的事情,當(dāng)網(wǎng)絡(luò)請求完成,得到下一個(gè)攔截器返回的response之后,判斷response的響應(yīng)碼是否是HTTP_NOT_MODIFIED = 304,(未改變)是則從緩存中讀取數(shù)據(jù)。
@Override
public Response intercept(Chain chain) throws IOException {
//通過request從緩存中獲取響應(yīng)(只會存在Get請求的緩存)
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
/**
* CacheStrategy 是一個(gè)緩存策略類 比如強(qiáng)制緩存 對比緩存等 它決定是使用緩存還是進(jìn)行網(wǎng)絡(luò)請求
* 其內(nèi)部維護(hù)了Request、Response
* 如果Request為null表示不使用網(wǎng)絡(luò)
* 如果Response為null表示不使用緩存
*/
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
//根據(jù)緩存策略獲取緩存Request和Response
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
//根據(jù)緩存策略,更新統(tǒng)計(jì)指標(biāo):請求次數(shù)、使用網(wǎng)絡(luò)請求次數(shù)、使用緩存次數(shù)
if (cache != null) {
cache.trackResponse(strategy);
}
// 能從緩存中獲取響應(yīng)但是緩存策略是不使用緩存,那就關(guān)閉獲取的緩存
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
// 根據(jù)策略,不使用網(wǎng)絡(luò),又沒有緩存的直接報(bào)錯(cuò),并返回錯(cuò)誤碼504
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// 如果緩存策略是不使用網(wǎng)絡(luò)但是可以使用緩存,那就通過緩存策略的緩存構(gòu)建響應(yīng)并返回
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
//緩存不可用或者緩存過期,網(wǎng)絡(luò)獲取
Response networkResponse = null;
try {
//前面兩個(gè)都沒有返回,繼續(xù)執(zhí)行下一個(gè)Interceptor,即ConnectInterceptor
networkResponse = chain.proceed(networkRequest);
} finally {
// 如果發(fā)生了IO異常或者其它異常,關(guān)閉緩存避免內(nèi)存泄漏
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// 如果緩存策略是可以使用緩存
if (cacheResponse != null) {
// 且網(wǎng)絡(luò)響應(yīng)碼是304 HTTP_NOT_MODIFIED說明本地緩存可以使用
// 且網(wǎng)絡(luò)響應(yīng)是沒有響應(yīng)體的
// 這時(shí)候就合并緩存響應(yīng)和網(wǎng)絡(luò)響應(yīng)并構(gòu)建新的響應(yīng)
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// 在合并標(biāo)頭之后但在剝離Content-Encoding標(biāo)頭之前更新緩存
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
// 走到這里說明緩存策略是不可以使用緩存或本地緩存不可用
// 那就通過網(wǎng)絡(luò)響應(yīng)構(gòu)建響應(yīng)對象
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
//對數(shù)據(jù)進(jìn)行緩存
if (cache != null) {
// 如果響應(yīng)有響應(yīng)體且響應(yīng)可以緩存 那就將響應(yīng)寫入到緩存
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// 緩存響應(yīng)的部分信息
CacheRequest cacheRequest = cache.put(response);
// 緩存響應(yīng)體并返回響應(yīng)
return cacheWritingResponse(cacheRequest, response);
}
//通過請求方法判斷需不需要進(jìn)行緩存
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
// 不合法就刪除緩存
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
//更新緩存
return response;
}
CacheInterceptor ,在發(fā)出請求前,判斷是否命中緩存。如果命中則可以不請求,直接使用緩存的響應(yīng)。 (只會存在Get請求的緩存)
步驟為:
- 1、從緩存中獲得對應(yīng)請求的響應(yīng)緩存
- 2、創(chuàng)建 CacheStrategy ,創(chuàng)建時(shí)會判斷是否能夠使用緩存,在 CacheStrategy 中存在兩個(gè)成員: networkRequest與 cacheResponse 。他們的組合如下:
攔截器通過CacheStrategy判斷使用緩存或發(fā)起網(wǎng)絡(luò)請求。此對象中的networkRequest與cacheResponse分別代表 需要發(fā)起請求或者直接使用緩存
即:networkRequest存在則優(yōu)先發(fā)起網(wǎng)絡(luò)請求,否則使用cacheResponse緩存,若都不存在則請求失?。?/p>
- 3、交給下一個(gè)責(zé)任鏈繼續(xù)處理
- 4、后續(xù)工作,返回304則用緩存的響應(yīng);否則使用網(wǎng)絡(luò)響應(yīng)并緩存本次響應(yīng)(只緩存Get請求的響應(yīng))
緩存攔截器的工作說起來比較簡單,但是具體的實(shí)現(xiàn),需要處理的內(nèi)容很多。在緩存攔截器中判斷是否可以使用緩存,或是請求服務(wù)器都是通過 CacheStrategy 判斷。
緩存策略
CacheStrategy 。首先需要認(rèn)識幾個(gè)請求頭與響應(yīng)頭


其中
Cache-Control可以在請求頭存在,也能在響應(yīng)頭存在,對應(yīng)的value可以設(shè)置多種組合
- 1、max-age=[秒] :資源最大有效時(shí)間;
- 2、public :表明該資源可以被任何用戶緩存,比如客戶端,代理服務(wù)器等都可以緩存資源;
- 3、
private:表明該資源只能被單個(gè)用戶緩存,默認(rèn)是private- 4、
no-store:資源不允許被緩存- 5、
no-cache:(請求)不使用緩存- 6、
immutable:(響應(yīng))資源不會改變- 7、
min-fresh=[秒]:(請求)用戶認(rèn)為這個(gè)緩存有效的時(shí)長- 8、
must-revalidate:(響應(yīng))不允許使用過期緩存- 9、
max-stale=[秒]:(請求)緩存過期后多久內(nèi)仍然有效假設(shè)存在max-age=100,min-fresh=20。這代表了用戶認(rèn)為這個(gè)緩存的響應(yīng),從服務(wù)器創(chuàng)建響應(yīng) 到 能夠緩存使用的時(shí)間為100-20=80s。但是如果max-stale=100。這代表了緩存有效時(shí)間80s過后,仍然允許使用100s,可以看成緩存有效時(shí)長為180s。

1.3.4、ConnectInterceptor 連接攔截器

在 okhttp底層是通過 socket 的方式于服務(wù)端進(jìn)行連接的,并且在連接建立之后會通過 okio 獲取通向 server 端的輸入流 Source 和輸出流 Sink。
@Override
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
//獲取可復(fù)用流
StreamAllocation streamAllocation = realChain.streamAllocation();
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//創(chuàng)建輸出流
HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
//根據(jù)HTTP/1.x(keep-alive)和HTTP/2(流復(fù)用)的復(fù)用機(jī)制,發(fā)起連接
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
雖然代碼量很少,實(shí)際上大部分功能都封裝到其它類去了,這里只是調(diào)用而已。
首先我們看到的 StreamAllocation 這個(gè)對象是在第一個(gè)攔截器:重定向攔截器創(chuàng)建的,但是真正使用的地方卻在這里。
"當(dāng)一個(gè)請求發(fā)出,需要建立連接,連接建立后需要使用流用來讀寫數(shù)據(jù)";而這個(gè)StreamAllocation就是協(xié)調(diào)請求、連接與數(shù)據(jù)流三者之間的關(guān)系,它負(fù)責(zé)為一次請求尋找連接,然后獲得流來實(shí)現(xiàn)網(wǎng)絡(luò)通信。
這里使用的 newStream 方法實(shí)際上就是去查找或者建立一個(gè)與請求主機(jī)有效的連接,返回的 HttpCodec 中包含了輸入輸出流,并且封裝了對HTTP請求報(bào)文的編碼與解碼,直接使用它就能夠與請求主機(jī)完成HTTP通信。
StreamAllocation 中簡單來說就是維護(hù)連接: RealConnection ——封裝了Socket與一個(gè)Socket連接池。可復(fù)用的 RealConnection 需要
public boolean isEligible(Address address, @Nullable Route route) {
// If this connection is not accepting new streams, we're done.
if (allocations.size() >= allocationLimit || noNewStreams) return false;
// If the non-host fields of the address don't overlap, we're done.
if (!Internal.instance.equalsNonHost(this.route.address(), address)) return false;
// If the host exactly matches, we're done: this connection can carry the address.
if (address.url().host().equals(this.route().address().url().host())) {
return true; // This connection is a perfect match.
}
// 1. This connection must be HTTP/2.
if (http2Connection == null) return false;
// 2. The routes must share an IP address. This requires us to have a DNS address for both
// hosts, which only happens after route planning. We can't coalesce connections that use a
// proxy, since proxies don't tell us the origin server's IP address.
if (route == null) return false;
if (route.proxy().type() != Proxy.Type.DIRECT) return false;
if (this.route.proxy().type() != Proxy.Type.DIRECT) return false;
if (!this.route.socketAddress().equals(route.socketAddress())) return false;
// 3. This connection's server certificate's must cover the new host.
if (route.address().hostnameVerifier() != OkHostnameVerifier.INSTANCE) return false;
if (!supportsUrl(address.url())) return false;
// 4. Certificate pinning must match the host.
try {
address.certificatePinner().check(address.url().host(), handshake().peerCertificates());
} catch (SSLPeerUnverifiedException e) {
return false;
}
return true; // The caller's address can be carried by this connection.
}
以上代碼解析:
if (allocations.size() >= allocationLimit || noNewStreams) return false;
連接到達(dá)最大并發(fā)流或者連接不允許建立新的流;如http1.x正在使用的連接不能給其他人用(最大并發(fā)流為:1)或者連接被關(guān)閉;那就不允許復(fù)用;
if (!Internal.instance.equalsNonHost(this.route.address(), address)) return false;
if (address.url().host().equals(this.route().address().url().host())) {
return true; // This connection is a perfect match.
}
DNS、代理、SSL證書、服務(wù)器域名、端口完全相同則可復(fù)用;
如果上述條件都不滿足,在HTTP/2的某些場景下可能仍可以復(fù)用(http2先不管)。
所以綜上,如果在連接池中找到個(gè)連接參數(shù)一致并且未被關(guān)閉沒被占用的連接,則可以復(fù)用。
總結(jié)
這個(gè)攔截器中的所有實(shí)現(xiàn)都是為了獲得一份與目標(biāo)服務(wù)器的連接,在這個(gè)連接上進(jìn)行HTTP數(shù)據(jù)的收發(fā)。
1.3.5、CallServerInterceptor 請求服務(wù)器攔截器(OkHttp核心攔截器,網(wǎng)絡(luò)交互的關(guān)鍵)
主要負(fù)責(zé)將請求寫入到 IO 流當(dāng)中,并且從 IO 流當(dāng)中獲取服務(wù)端返回給客服端的響應(yīng)數(shù)據(jù)。
CallServerInterceptor 在 ConnectInterceptor 攔截器的功能就是負(fù)責(zé)與服務(wù)器建立 Socket 連接,并且創(chuàng)建了一個(gè) HttpStream 它包括通向服務(wù)器的輸入流和輸出流。而接下來的 CallServerInterceptor 攔截器的功能使用 HttpStream 與服務(wù)器進(jìn)行數(shù)據(jù)的讀寫操作的
okhttp的攔截器就是在intercept(Chain chain)的回調(diào)中對Request和Response進(jìn)行修改,然后直接返回了response 而不是進(jìn)行繼續(xù)遞歸,具體執(zhí)行RealConnection里面是通過OKio實(shí)現(xiàn)的。在okhttp中,網(wǎng)絡(luò)連接也是一個(gè)攔截器(CallServerInterceptor),他是最后一個(gè)被調(diào)用的,負(fù)責(zé)將request寫入網(wǎng)絡(luò)流中,并從網(wǎng)絡(luò)流中讀取服務(wù)器返回的信息寫入Response中返回給客戶端。
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
//發(fā)送請求的時(shí)間戳
long sentRequestMillis = System.currentTimeMillis();
//寫入請求頭信息
httpCodec.writeRequestHeaders(request);
//發(fā)送header數(shù)據(jù)
httpCodec.writeRequestHeaders(request);
Response.Builder responseBuilder = null;
//根據(jù)是否支持100-continue,發(fā)送body數(shù)據(jù)
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!connection.isMultiplexed()) {
// HTTP2多路復(fù)用,不需要關(guān)閉socket,不管!.
streamAllocation.noNewStreams();
}
}
//結(jié)束請求
httpCodec.finishRequest();
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
//讀取響應(yīng)頭信息
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
//發(fā)送請求的時(shí)間
.sentRequestAtMillis(sentRequestMillis)
//接收到響應(yīng)的時(shí)間
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
//response處理
if (code == 100) {
responseBuilder = httpCodec.readResponseHeaders(false);
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);
if (forWebSocket && code == 101) {
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
上面代碼解析流程:
CallServerInterceptor ,利用 HttpCodec 發(fā)出請求到服務(wù)器并且解析生成 Response 。
首先調(diào)用 httpCodec.writeRequestHeaders(request); 將請求頭寫入到緩存中(直到調(diào)用 flushRequest() 才真正發(fā)送給服務(wù)器)。然后馬上進(jìn)行第一個(gè)邏輯判斷。
//根據(jù)是否支持100-continue,發(fā)送body數(shù)據(jù)
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!connection.isMultiplexed()) {
// HTTP2多路復(fù)用,不需要關(guān)閉socket,不管!.
streamAllocation.noNewStreams();
}
}
整個(gè)if都和一個(gè)請求頭有關(guān): Expect: 100-continue 。這個(gè)請求頭代表了在發(fā)送請求體之前需要和服務(wù)器確定是否愿意接受客戶端發(fā)送的請求體。所以 permitsRequestBody 判斷為是否會攜帶請求體的方式(POST),如果命中 if,則會先給服務(wù)器發(fā)起一次查詢是否愿意接收請求體,這時(shí)候如果服務(wù)器愿意會響應(yīng)100(沒有響應(yīng)體,responseBuilder 即為nul)。這時(shí)候才能夠繼續(xù)發(fā)送剩余請求數(shù)據(jù)。
但是如果服務(wù)器不同意接受請求體,那么我們就需要標(biāo)記該連接不能再被復(fù)用,調(diào)用 noNewStreams() 關(guān)閉相關(guān)的 Socket。
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
//讀取響應(yīng)頭信息
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
//發(fā)送請求的時(shí)間
.sentRequestAtMillis(sentRequestMillis)
//接收到響應(yīng)的時(shí)間
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
這時(shí) responseBuilder 的情況即為:
- 1、POST方式請求,請求頭中包含 Expect ,服務(wù)器允許接受請求體,并且已經(jīng)發(fā)出了請求體, responseBuilder為null;
- 2、POST方式請求,請求頭中包含 Expect ,服務(wù)器不允許接受請求體, responseBuilder 不為null
- 3、POST方式請求,未包含 Expect ,直接發(fā)出請求體, responseBuilder 為null;
- 4、POST方式請求,沒有請求體, responseBuilder 為null;
- 5、GET方式請求, responseBuilder 為null;
對應(yīng)上面的5種情況,讀取響應(yīng)頭并且組成響應(yīng) Response ,注意:此 Response 沒有響應(yīng)體。同時(shí)需要注意的是,如果服務(wù)器接受 Expect: 100-continue 這是不是意味著我們發(fā)起了兩次 Request ?那此時(shí)的響應(yīng)頭是第一次查詢服務(wù)器是否支持接受請求體的,而不是真正的請求對應(yīng)的結(jié)果響應(yīng)。所以緊接著:
int code = response.code();
//response處理
if (code == 100) {
responseBuilder = httpCodec.readResponseHeaders(false);
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
如果響應(yīng)是100,這代表了是請求 Expect: 100-continue 成功的響應(yīng),需要馬上再次讀取一份響應(yīng)頭,這才是真正的請求對應(yīng)結(jié)果響應(yīng)頭。
if (forWebSocket && code == 101) {
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
forWebSocket 代表websocket的請求,我們直接進(jìn)入else,這里就是讀取響應(yīng)體數(shù)據(jù)。然后判斷請求和服務(wù)器是不是都希望長連接,一旦有一方指明 close ,那么就需要關(guān)閉 socket 。而如果服務(wù)器返回204/205,一般情況而言不會存在這些返回碼,但是一旦出現(xiàn)這意味著沒有響應(yīng)體,但是解析到的響應(yīng)頭中包含 Content-Lenght 且不為 0,這表響應(yīng)體的數(shù)據(jù)字節(jié)長度。此時(shí)出現(xiàn)了沖突,直接拋出協(xié)議異常
二、 RealInterceptorChain 攔截器鏈

- 當(dāng)發(fā)送一個(gè)請求的時(shí)候,實(shí)質(zhì)OkHttp會通過一個(gè)攔截器的鏈來執(zhí)行OkHttp的請求。
- 這就是所謂的攔截器鏈,執(zhí)行 RetryAndFollowUpInterceptor => 執(zhí)行 BridgeInterceptor => 執(zhí)行 CacheInterceptor => 執(zhí)行 ConnectInterceptor => 執(zhí)行 CallServerInterceptor => 響應(yīng)到
ConnectInterceptor => 響應(yīng)到 CacheInterceptor => 響應(yīng)到 BridgeInterceptor => 響應(yīng)到 RetryAndFollowUpInterceptor
public final class RealInterceptorChain implements Interceptor.Chain {
private final List<Interceptor> interceptors;
//在RetryAndFollowUpInterceptor中new的
private final StreamAllocation streamAllocation;
//在ConnectInterceptor中new的
private final HttpCodec httpCodec;
//在ConnectInterceptor中new的
private final RealConnection connection;
//標(biāo)識應(yīng)該取攔截器鏈表里面的第幾個(gè)攔截器
private final int index; //通過index + 1
private int calls; //通過call++
private final Request request;
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
}
@Override public Connection connection() {
return connection;
}
public StreamAllocation streamAllocation() {
return streamAllocation;
}
public HttpCodec httpStream() {
return httpCodec;
}
@Override public Request request() {
return request;
}
//執(zhí)行繼續(xù)攔截操作
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
/**
* 依次取出攔截器鏈表中的每個(gè)攔截器去獲取Response
*/
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
// 1、迭代攔截器集合
if (index >= interceptors.size()) throw new AssertionError();
//2、記錄本方法調(diào)用次數(shù),創(chuàng)建一次實(shí)例,call加1
calls++;
//如果已經(jīng)為該Request創(chuàng)建了stream,就不再繼續(xù)創(chuàng)建了
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// 如果已經(jīng)為該Request創(chuàng)建了stream,那該方法只能調(diào)用一次
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
//創(chuàng)建新的攔截器鏈對象, 并將計(jì)數(shù)器+1
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
//取出下一個(gè)攔截器
Interceptor interceptor = interceptors.get(index);
//執(zhí)行攔截器的intercept方法獲取結(jié)果,并將新的攔截器鏈對象傳入
Response response = interceptor.intercept(next);
// 確保該方法只能調(diào)用一次
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
}
三、總結(jié)
在這個(gè)攔截器中就是完成HTTP協(xié)議報(bào)文的封裝與解析。



