1.okhttp源碼分析(一)——基本流程(超詳細(xì))
2.okhttp源碼分析(二)——RetryAndFollowUpInterceptor過濾器
3.okhttp源碼分析(三)——CacheInterceptor過濾器
4.okhttp源碼分析(四)——ConnectInterceptor過濾器
5.okhttp源碼分析(五)——CallServerInterceptor過濾器
前言
緊接著上一篇基本流程分析完后,準(zhǔn)備的是將過濾器分析分析复局,過濾器可以說是OkHttp的點(diǎn)睛之筆。這一篇主要分析RetryAndFollowUpInterceptor這個(gè)過濾器,首先從名字我們就可以明白這個(gè)過濾器的職責(zé)是重試和重定向孕豹。
分析
1.宏觀流程
讀過上一篇博客的人應(yīng)該都清楚看一個(gè)過濾器的功能集嵌,重點(diǎn)都在他重寫Interceptor的intercept(Chain chain)
方法妻味。
打開源碼突然發(fā)現(xiàn)這么長(zhǎng)。乍炉。。不要絕望滤馍,我按照我的理解將代碼處理一下岛琼,我們先從流程看起,至少?gòu)暮暧^上要對(duì)這個(gè)過濾器有個(gè)認(rèn)識(shí)巢株。
@Override public Response intercept(Chain chain) throws IOException {
槐瑞。。阁苞。
while (true) {
困檩。。猬错。
try {
response = realChain.proceed(request, streamAllocation, null, null);
}
窗看。。倦炒。
if(滿足條件){
return response;
}
显沈。。逢唤。
//不滿足條件拉讯,一頓操作,賦值再來鳖藕!
request = followUp;
priorResponse = response;
}
}
其實(shí)就流程來上魔慷,我認(rèn)為宏觀上代碼縮減到這樣就夠了,甚至可以再刪點(diǎn)著恩,這里先從流程上理解.
其實(shí)代碼成這樣院尔,基本上大家都能理解了,一個(gè)while(true)表明這是個(gè)循環(huán)體喉誊,循環(huán)體主要做的事可以看到其實(shí)是遞歸的主要方法邀摆。
response = realChain.proceed(request, streamAllocation, null, null);
執(zhí)行了這個(gè)方法后,就會(huì)交給下一個(gè)過濾器繼續(xù)執(zhí)行伍茄,所以單從這里來看栋盹,我們可以簡(jiǎn)單的理解為這個(gè)過濾器其實(shí)沒做什么。
但是當(dāng)出現(xiàn)了一些問題敷矫,導(dǎo)致不滿足條件的時(shí)候例获,就需要進(jìn)行一系列的操作汉额,重新復(fù)制Request,重新請(qǐng)求榨汤,這也就是while的功能蠕搜,對(duì)應(yīng)的也就是這個(gè)過濾器的主要功能:重試和重定向。
這里我們宏觀上已經(jīng)對(duì)RetryAndFollowUpInterceptor有了一個(gè)基本的理解了件余。
2.過程細(xì)節(jié)
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
//streamAllocation的創(chuàng)建位置
streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
call, eventListener, callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
while (true) {
//取消
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
//重試讥脐。。啼器。
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
//先判斷當(dāng)前請(qǐng)求是否已經(jīng)發(fā)送了
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
//同樣的重試判斷
if (!recover(e, requestSendStarted, request)) throw e;
releaseConnection = false;
//重試旬渠。。端壳。
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
//沒有捕獲到的異常告丢,最終要釋放
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
//這里基本上都沒有講,priorResponse是用來保存前一個(gè)Resposne的损谦,這里可以看到將前一個(gè)Response和當(dāng)前的Resposne
//結(jié)合在一起了岖免,對(duì)應(yīng)的場(chǎng)景是,當(dāng)獲得Resposne后照捡,發(fā)現(xiàn)需要重定向颅湘,則將當(dāng)前Resposne設(shè)置給priorResponse,再執(zhí)行一遍流程栗精,
//直到不需要重定向了闯参,則將priorResponse和Resposne結(jié)合起來。
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
//判斷是否需要重定向,如果需要重定向則返回一個(gè)重定向的Request悲立,沒有則為null
Request followUp = followUpRequest(response);
if (followUp == null) {
//不需要重定向
if (!forWebSocket) {
//是WebSocket,釋放
streamAllocation.release();
}
//返回response
return response;
}
//需要重定向鹿寨,關(guān)閉響應(yīng)流
closeQuietly(response.body());
//重定向次數(shù)++,并且小于最大重定向次數(shù)MAX_FOLLOW_UPS(20)
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
//是UnrepeatableRequestBody, 剛才看過也就是是流類型薪夕,沒有被緩存脚草,不能重定向
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
//判斷是否相同,不然重新創(chuàng)建一個(gè)streamConnection
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
//賦值再來原献!
request = followUp;
priorResponse = response;
}
}
現(xiàn)在就要從源碼上具體學(xué)習(xí)理解這個(gè)過濾器了馏慨。這里我具體一點(diǎn)一點(diǎn)分析。
//streamAllocation的創(chuàng)建位置
streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
call, eventListener, callStackTrace);
首先第一個(gè)點(diǎn)姑隅,這里可以看到這里對(duì)streamAllocation進(jìn)行了初始化操作熏纯,其實(shí)在過濾器的鏈?zhǔn)秸{(diào)用的過程中會(huì)陸陸續(xù)續(xù)創(chuàng)建一系列對(duì)應(yīng)的參數(shù),這一點(diǎn)從最初的創(chuàng)建Chain的時(shí)候就可以看出來粤策,可以看到一開始有很多參數(shù)是以null傳入的。
Response getResponseWithInterceptorChain() throws IOException {
误窖。叮盘。秩贰。
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
。柔吼。毒费。
return chain.proceed(originalRequest);
}
這里先大概說一下StreamAllocation這個(gè)對(duì)象是干什么的。這個(gè)類大概可以理解為是處理Connections,Streams,Calls三者之間的關(guān)系愈魏,這一點(diǎn)其實(shí)從構(gòu)造函數(shù)的傳參也可以看出來觅玻。
接下來就要進(jìn)入循環(huán)體中看了,首先可以看到當(dāng)請(qǐng)求被取消的時(shí)候培漏,會(huì)跳出循環(huán)體(第一種跳出的情況)溪厘。
boolean releaseConnection = true;
try {
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
//重試。牌柄。畸悬。
continue;
}
接下來看try catch體中的內(nèi)容,try其實(shí)很簡(jiǎn)單珊佣,就是執(zhí)行后續(xù)過濾器鏈中的東西蹋宦,這里要稍微注意一下releaseConnection這個(gè)變量的,對(duì)后續(xù)的判斷理解是有影響的咒锻,可以看到初始化時(shí)將releaseConnection這個(gè)變量賦值為true冷冗。
下面是重點(diǎn)內(nèi)容了:
進(jìn)入catch體中,可以看到會(huì)捕獲很多okHttp自定義的Exception惑艇,從名字上可以有一個(gè)大體上的理解蒿辙,但是還是要從源碼上分析,這里先看第一個(gè)異常RouteException敦捧,先理解理解注釋:嘗試連接一個(gè)路由失敗须板,這個(gè)請(qǐng)求還沒有被發(fā)出,接下來執(zhí)行了一個(gè)方法recover(),這里注意一下false參數(shù)兢卵,現(xiàn)在進(jìn)入方法體中习瑰。
private boolean recover(IOException e, boolean requestSendStarted, Request userRequest) {
streamAllocation.streamFailed(e);
// The application layer has forbidden retries.
//如果OkHttpClient直接配置拒絕失敗重連,return false
if (!client.retryOnConnectionFailure()) return false;
// We can't send the request body again.
//如果請(qǐng)求已經(jīng)發(fā)送秽荤,并且這個(gè)請(qǐng)求體是一個(gè)UnrepeatableRequestBody類型甜奄,則不能重試。
//StreamedRequestBody實(shí)現(xiàn)了UnrepeatableRequestBody接口窃款,是個(gè)流類型课兄,不會(huì)被緩存,所以只能執(zhí)行一次晨继,具體可看烟阐。
if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) return false;
// This exception is fatal.
//一些嚴(yán)重的問題,就不要重試了
if (!isRecoverable(e, requestSendStarted)) return false;
// No more routes to attempt.
//沒有更多的路由就不要重試了
if (!streamAllocation.hasMoreRoutes()) return false;
// For failure recovery, use the same route selector with a new connection.
return true;
}
可以看到這里面有很多的if判斷,這里先看第一個(gè)蜒茄。
if (!client.retryOnConnectionFailure()) return false;
//==========OkHttpClient.java===========
public boolean retryOnConnectionFailure() {
return retryOnConnectionFailure;
}
代碼一放上來其實(shí)就很好理解了唉擂,如果我們?cè)谂渲肙kHttpClient中配置retryOnConnectionFailure屬性為false,表明拒絕失敗重連檀葛,那么這里返回false(第一種拒絕重連的方式)玩祟。這里另外說明一下如果我們默認(rèn)方式創(chuàng)建OkHttpClient的話,retryOnConnectionFailure屬性是true屿聋。
if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) return false;
下面一個(gè)判斷首先要明白參數(shù)的含義空扎,這里requestSendStarted這個(gè)參數(shù)就是剛才在recover方法中的第二個(gè)參數(shù),是為了表明請(qǐng)求是否已經(jīng)被發(fā)送润讥,這里這里為false转锈,但是這個(gè)判斷我們需要了解清楚。
單單從判斷條件我們可以理解為:如果請(qǐng)求已經(jīng)發(fā)送象对,并且這個(gè)請(qǐng)求體是一個(gè)UnrepeatableRequestBody類型黑忱,則不能重試(第二種拒絕重連的方式)。
這里就要說明一下UnrepeatableRequestBody這個(gè)類了勒魔。
public interface UnrepeatableRequestBody {
}
這就是UnrepeatableRequestBody的源碼甫煞,沒有看錯(cuò)...就是一個(gè)空的接口,作用就是標(biāo)記那些不能被重復(fù)請(qǐng)求的請(qǐng)求體冠绢,這時(shí)候可能就想要了解一下那些請(qǐng)求是不能被重復(fù)請(qǐng)求的哪抚吠?看一下那些Request實(shí)現(xiàn)了這個(gè)接口,結(jié)果會(huì)發(fā)現(xiàn)弟胀,到目前Okhttp源碼中楷力,只有一種請(qǐng)求實(shí)現(xiàn)了這個(gè)接口,那就是StreamedRequestBody孵户。
/**
* This request body streams bytes from an application thread to an OkHttp dispatcher thread via a
* pipe. Because the data is not buffered it can only be transmitted once.
*/
final class StreamedRequestBody extends OutputStreamRequestBody implements UnrepeatableRequestBody {}
從這個(gè)類的注釋我們也可以理解萧朝,StreamedRequestBody實(shí)現(xiàn)了UnrepeatableRequestBody接口,是個(gè)流類型夏哭,不會(huì)被緩存检柬,所以只能執(zhí)行一次。
if (!isRecoverable(e, requestSendStarted)) return false;
//=====================isRecoverable()=====================
private boolean isRecoverable(IOException e, boolean requestSendStarted) {
// If there was a protocol problem, don't recover.
//如果是協(xié)議問題竖配,不要在重試了
if (e instanceof ProtocolException) {
return false;
}
// If there was an interruption don't recover, but if there was a timeout connecting to a route
// we should try the next route (if there is one).
if (e instanceof InterruptedIOException) {
//超時(shí)問題何址,并且請(qǐng)求還沒有被發(fā)送,可以重試
//其他就不要重試了
return e instanceof SocketTimeoutException && !requestSendStarted;
}
// Look for known client-side or negotiation errors that are unlikely to be fixed by trying
// again with a different route.
if (e instanceof SSLHandshakeException) {
// If the problem was a CertificateException from the X509TrustManager,
// do not retry.
//理解為如果是安全原因进胯,就不要重試了
if (e.getCause() instanceof CertificateException) {
return false;
}
}
if (e instanceof SSLPeerUnverifiedException) {
// e.g. a certificate pinning error.
//安全原因
return false;
}
// An example of one we might want to retry with a different route is a problem connecting to a
// proxy and would manifest as a standard IOException. Unless it is one we know we should not
// retry, we return true and try a new route.
return true;
}
下面一個(gè)判斷可以總體理解為:如果是一些嚴(yán)重的問題(協(xié)議用爪,安全...),拒絕重試(第三種拒絕重連的方式)
這里可以看到判斷被歸結(jié)到一個(gè)isRecoverable()的方法中胁镐,注釋頁(yè)寫的很清楚偎血,這里嚴(yán)重的情況主要由這幾種:
- 1.協(xié)議問題诸衔,不能重試。
- 2.如果是超時(shí)問題颇玷,并且請(qǐng)求沒有被發(fā)送署隘,可以重試,其他的就不要重試了亚隙。
- 3.安全問題,不要重試违崇。
if (!streamAllocation.hasMoreRoutes()) return false;
//========================StreamAllocation.java=====================
public boolean hasMoreRoutes() {
return route != null
|| (routeSelection != null && routeSelection.hasNext())
|| routeSelector.hasNext();
}
下面這個(gè)判斷表明:沒有更多的可以使用的路由阿弃,則不要重試了(第四種拒絕重連的方式)
這里也列出了hasMoreRoutes()方法,可以看到羞延,這里面當(dāng)游標(biāo)在最末尾渣淳,也就是保存的路由的容器已經(jīng)遍歷完了,也就沒辦法繼續(xù)重試了伴箩。這里大概說明一下routeSelection是用List保存的入愧。
catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
//重試。嗤谚。棺蛛。
continue;
}
所以當(dāng)以上判斷結(jié)束后,如果需要重試巩步,則continue旁赊,重新執(zhí)行while循環(huán)體,也就是發(fā)揮了這個(gè)過濾器的作用椅野,重試
catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
//先判斷當(dāng)前請(qǐng)求是否已經(jīng)發(fā)送了
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
//同樣的重試判斷
if (!recover(e, requestSendStarted, request)) throw e;
releaseConnection = false;
//重試终畅。。竟闪。
continue;
}
這時(shí)候看一下下一個(gè)異常IOException离福,首先可以看到,需要先判斷請(qǐng)求是否已經(jīng)發(fā)送了炼蛤,緊接著繼續(xù)剛才分析的方法recover(),這時(shí)默認(rèn)傳的就不是false妖爷,而是判斷得到的requestSendStarted。最后同樣當(dāng)需要重試時(shí)鲸湃,continue赠涮。
finally {
// We're throwing an unchecked exception. Release any resources.
//沒有捕獲到的異常,最終要釋放
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
finally體中的內(nèi)容比較好理解暗挑,由于releaseConnection初始化為true笋除,而當(dāng)正常執(zhí)行realChain.proceed
或在執(zhí)行過程中捕捉到異常時(shí)設(shè)置為false,所以當(dāng)執(zhí)行過程中捕捉到?jīng)]有檢測(cè)到的異常時(shí)炸裆,需要釋放一些內(nèi)容垃它。(此處感謝@messi_wpy的指正)
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
接下來這段代碼一開始是我比較難以理解的,而且網(wǎng)上其他分析這個(gè)過濾器的都沒有分析這塊,最后自己分析国拇,理解為priorResponse是用來保存前一個(gè)Resposne的洛史,這里可以看到將前一個(gè)Response和當(dāng)前的Resposne結(jié)合在一起了。對(duì)應(yīng)的場(chǎng)景是:當(dāng)獲得Resposne后酱吝,發(fā)現(xiàn)需要重定向也殖,則將當(dāng)前Resposne設(shè)置給priorResponse,再執(zhí)行一遍流程务热,直到不需要重定向了忆嗜,則將priorResponse和Resposne結(jié)合起來。
Request followUp = followUpRequest(response);
//=========================followUpRequest()==============================
private Request followUpRequest(Response userResponse) throws IOException {
if (userResponse == null) throw new IllegalStateException();
Connection connection = streamAllocation.connection();
Route route = connection != null
? connection.route()
: null;
int responseCode = userResponse.code();
final String method = userResponse.request().method();
switch (responseCode) {
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);
case HTTP_UNAUTHORIZED:
return client.authenticator().authenticate(route, userResponse);
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
// or HEAD, the user agent MUST NOT automatically redirect the request"
if (!method.equals("GET") && !method.equals("HEAD")) {
return null;
}
// fall-through
case HTTP_MULT_CHOICE:
case HTTP_MOVED_PERM:
case HTTP_MOVED_TEMP:
case HTTP_SEE_OTHER:
// Does the client allow redirects?
if (!client.followRedirects()) return null;
String location = userResponse.header("Location");
if (location == null) return null;
HttpUrl url = userResponse.request().url().resolve(location);
// Don't follow redirects to unsupported protocols.
if (url == null) return null;
// If configured, don't follow redirects between SSL and non-SSL.
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();
if (HttpMethod.permitsRequestBody(method)) {
final boolean maintainBody = HttpMethod.redirectsWithBody(method);
if (HttpMethod.redirectsToGet(method)) {
requestBuilder.method("GET", null);
} else {
RequestBody requestBody = maintainBody ? userResponse.request().body() : null;
requestBuilder.method(method, requestBody);
}
if (!maintainBody) {
requestBuilder.removeHeader("Transfer-Encoding");
requestBuilder.removeHeader("Content-Length");
requestBuilder.removeHeader("Content-Type");
}
}
// When redirecting across hosts, drop all authentication headers. This
// is potentially annoying to the application layer since they have no
// way to retain them.
if (!sameConnection(userResponse, url)) {
requestBuilder.removeHeader("Authorization");
}
//重新構(gòu)造了一個(gè)Request
return requestBuilder.url(url).build();
case HTTP_CLIENT_TIMEOUT:
// 408's are rare in practice, but some servers like HAProxy use this response code. The
// spec says that we may repeat the request without modifications. Modern browsers also
// repeat the request (even non-idempotent ones.)
if (!client.retryOnConnectionFailure()) {
// The application layer has directed us not to retry the request.
return null;
}
if (userResponse.request().body() instanceof UnrepeatableRequestBody) {
return null;
}
if (userResponse.priorResponse() != null
&& userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {
// We attempted to retry and got another timeout. Give up.
return null;
}
return userResponse.request();
default:
return null;
}
}
下面這行代碼主要是對(duì)followUpRequest()這個(gè)方法的理解崎岂,代碼我也粘出來了捆毫,這里其實(shí)沒必要在意每一行代碼,這樣反而影響我們閱讀冲甘,這里主要可以觀察發(fā)現(xiàn)绩卤,其實(shí)這個(gè)方法的主要操作就是,當(dāng)返回碼滿足某些條件時(shí)就重新構(gòu)造一個(gè)Request江醇,不滿足就返回null,所以接下來的代碼就很容易理解了濒憋。
if (followUp == null) {
//不需要重定向
if (!forWebSocket) {
//是WebSocket,釋放
streamAllocation.release();
}
//返回response
return response;
}
當(dāng)不需要重定向,也就是返回的為null,直接返回response嫁审。
//需要重定向跋炕,關(guān)閉響應(yīng)流
closeQuietly(response.body());
//重定向次數(shù)++,并且小于最大重定向次數(shù)MAX_FOLLOW_UPS(20)
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
//是UnrepeatableRequestBody, 剛才看過也就是是流類型律适,沒有被緩存辐烂,不能重定向
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
//判斷是否相同,不然重新創(chuàng)建一個(gè)streamConnection
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
//賦值再來捂贿!
request = followUp;
priorResponse = response;
//==========================sameConnection()======================
private boolean sameConnection(Response response, HttpUrl followUp) {
HttpUrl url = response.request().url();
return url.host().equals(followUp.host())
&& url.port() == followUp.port()
&& url.scheme().equals(followUp.scheme());
}
下面的代碼當(dāng)然就是當(dāng)返回的不為空纠修,也就是重新構(gòu)造了一個(gè)Request,需要重定向厂僧。
- 1.首先關(guān)閉響應(yīng)流扣草。
- 2.增加重定向的次數(shù),保證小于最大重定向次數(shù)MAX_FOLLOW_UPS(20)
- 3.不能是UnrepeatableRequestBody類型颜屠,剛才也分析過辰妙,是一個(gè)空接口,用于標(biāo)記那些只能請(qǐng)求一次的請(qǐng)求甫窟。
- 4.判斷是否相同密浑,如果不相同,則需要重新創(chuàng)建一個(gè)streamConnection粗井。
- 5.重新賦值尔破,結(jié)束當(dāng)前循環(huán)街图,繼續(xù)while循環(huán),也就是執(zhí)行重定向請(qǐng)求懒构。
總結(jié)
到此餐济,RetryAndFollowUpInterceptor這個(gè)過濾器已經(jīng)大體分析完了,總體流程下來可以發(fā)現(xiàn)胆剧,這個(gè)過濾器的主要作用就是用于對(duì)請(qǐng)求的重試和重定向的絮姆。
其中拒絕重試的判斷條件有如下幾種:
- 1.如果我們?cè)谂渲肙kHttpClient中配置retryOnConnectionFailure屬性為false,表明拒絕失敗重連秩霍,那么這里返回false
- 2.如果請(qǐng)求已經(jīng)發(fā)送滚朵,并且這個(gè)請(qǐng)求體是一個(gè)UnrepeatableRequestBody類型,則不能重試
- 3.如果是一些嚴(yán)重的問題(協(xié)議前域,安全...),拒絕重試
- 4.沒有更多的可以使用的路由韵吨,則不要重試了