核心功能
1.連接失敗重試(Retry)
在發生 RouteException 或者 IOException 後,會捕獲建聯或者讀取的一些異常,根據一定的策略判斷是否是可恢復的,如果可恢復會重新創建 StreamAllocation 開始新的一輪請求
2.繼續發起請求(Follow up)
主要有這幾種類型
3xx 重定向
401,407 未授權,調用 Authenticator 進行授權後繼續發起新的請求
408 客戶端請求超時,如果 Request 的請求體沒有被 UnrepeatableRequestBody 標記,會繼續發起新的請求
其中 Follow up 的次數受到MAX_FOLLOW_UP 約束,在 OkHttp 中爲 20 次,這樣可以防止重定向死循環
流程圖
源碼分析
@Override public Response intercept(Chain chain) throws IOException {
// 1. 獲取 Transmitter對象
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();
int followUpCount = 0;
Response priorResponse = null;
while (true) {
transmitter.prepareToConnect(request);
if (transmitter.isCanceled()) {
throw new IOException("Canceled");
}
Response response;
boolean success = false;
try {
// 2. 執行下一個攔截器,即BridgeInterceptor
response = realChain.proceed(request, transmitter, null);
success = true;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
// 3. 如果有異常,判斷是否要恢復
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, transmitter, requestSendStarted, request)) throw e;
continue;
} finally {
// The network call threw an exception. Release any resources.
if (!success) {
transmitter.exchangeDoneDueToException();
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Exchange exchange = Internal.instance.exchange(response);
Route route = exchange != null ? exchange.connection().route() : null;
// 檢查是否符合重定向要求
Request followUp = followUpRequest(response, route);
if (followUp == null) {
if (exchange != null && exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
// 返回結果
return response;
}
RequestBody followUpBody = followUp.body();
if (followUpBody != null && followUpBody.isOneShot()) {
return response;
}
// 5.關閉響應流
closeQuietly(response.body());
if (transmitter.hasExchange()) {
exchange.detachWithViolence();
}
//最大限制判斷
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
request = followUp;
priorResponse = response;
}
}
這就是RetryAndFollowUpInterceptor的核心代碼 我們來一步步的分析
1.獲取一個Transmitter 對象
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();
他是網絡請求的調度器,作用在整個網絡請求生命週期 它用於協調Connection,Stream,Call。他的創建是在newRealCall的時候
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.transmitter = new Transmitter(client, call);
return call;
}
2.開啓循環,執行下一個調用鏈(攔截器),等待返回結果(Response)
response = realChain.proceed(request, streamAllocation, null, null);
3.如果catch了進入recover 判斷收可以重新請求
private boolean recover(IOException e, Transmitter transmitter,
boolean requestSendStarted, Request userRequest) {
// The application layer has forbidden retries.
//如果OkHttpClient直接配置拒絕失敗重連,return false
if (!client.retryOnConnectionFailure()) return false;
// We can't send the request body again.
//如果請求已經發送,並且這個請求體是一個只能發送一次類型,則不能重試。
if (requestSendStarted && requestIsOneShot(e, userRequest)) return false;
// This exception is fatal.
if (!isRecoverable(e, requestSendStarted)) return false;
// No more routes to attempt.
//發射器不能夠重試
if (!transmitter.canRetry()) return false;
// For failure recovery, use the same route selector with a new connection.
return true;
}
無法重試的isRecoverable()原因
private boolean isRecoverable(IOException e, boolean requestSendStarted) {
// If there was a protocol problem, don't recover.
//如果是協議問題,不要在重試了
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) {
//超時問題,並且請求還沒有被髮送,可以重試
//其他就不要重試了
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;
}
4.關閉響應流
closeQuietly(response.body());
5.增加重定向次數
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
```、
6.繼續循環知道返回response或者 拋出異常
到這裏RetryAndFollowUpInterceptor的分析就結束了進入下一個。
最後獻上一份添加了註釋的源碼 [https://github.com/525642022/okhttpTest/blob/master/README.md](https://github.com/525642022/okhttpTest/blob/master/README.md)
哈哈