看一下OkHttp内置的第一個攔截器RetryAndFollowUpInterceptor:
RetryAndFollowUpInterceptor
public final class RetryAndFollowUpInterceptor implements Interceptor {
private static final int MAX_FOLLOW_UPS = 20; //最大的重定向次數
private final OkHttpClient client; //前面建立的OkHttpClient對象
private final boolean forWebSocket; //是否是webSocket
private StreamAllocation streamAllocation; //流配置設定器,在這個攔截器中初始化
private Object callStackTrace;
private volatile boolean canceled; //請求是否被取消了
//主要是傳入進來OkHttpClient對象
public RetryAndFollowUpInterceptor(OkHttpClient client, boolean forWebSocket) {
this.client = client;
this.forWebSocket = forWebSocket;
}
@Override public Response intercept(Chain chain) throws IOException {
//***************擷取需要的對象Request,RealInterceptorChain,Call,StreamAllocation****************
Request request = chain.request(); //之前我們建立的Request對象
RealInterceptorChain realChain = (RealInterceptorChain) chain;//建立的下一個攔截器鍊
Call call = realChain.call(); //我們之前建立的RealCall
EventListener eventListener = realChain.eventListener();
//建立了一個StreamAllocation對象,持有全局的連接配接池,Address對象,Call對象
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.
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.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
//**********************解析回複的code,如果請求失敗,進行重定向*******************
//解析response的code,判斷response的類型,看是否請求成功。
Request followUp = followUpRequest(response);
//如果請求成功,傳回followUp Request為null
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
//請求成功,傳回這個response
return response;
}
//請求不成功,這個response關閉,釋放資源。
closeQuietly(response.body());
//followUpCount加1後判斷是否大于MAX_FOLLOW_UPS
if (++followUpCount > MAX_FOLLOW_UPS) {
//重試次數超過了限制,不再嘗試,釋放資源
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
//不能繼續嘗試了
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
//看response的伺服器位址和followUp Request的url對比,看位址是否相同,這涉及是否可以連接配接複用。
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
//重新建立一個StreamAllocation對象
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對象
request = followUp;
//記錄Response
priorResponse = response;
}
/**
* 取消執行請求
*/
public void cancel() {
canceled = true;
StreamAllocation streamAllocation = this.streamAllocation;
if (streamAllocation != null) streamAllocation.cancel();
}
//傳回是否被取消
public boolean isCanceled() {
return canceled;
}
}
上述過程中出現了StreamAllocation對象的建立,以及調用release方法等。下面看一下StreamAllocation這個類:
StreamAllocation
public final class StreamAllocation {
public final Address address; //請求位址資訊
private RouteSelector.Selection routeSelection;//路由選擇器
private Route route; //路由
private final ConnectionPool connectionPool; //全局連接配接池,在OkHttpClient對象建立過程中已經建立完成
public final Call call; //請求Call
public final EventListener eventListener;
private final Object callStackTrace;
// State guarded by connectionPool.
private final RouteSelector routeSelector;
private int refusedStreamCount;
private RealConnection connection; //真實建立的連接配接
private boolean reportedAcquired;
private boolean released; //資源是否被釋放
private boolean canceled; //請求是否被取消
private HttpCodec codec; //Http 流
public StreamAllocation(ConnectionPool connectionPool, Address address, Call call,
EventListener eventListener, Object callStackTrace) {
this.connectionPool = connectionPool;
this.address = address;
this.call = call;
this.eventListener = eventListener;
this.routeSelector = new RouteSelector(address, routeDatabase(), call, eventListener);
this.callStackTrace = callStackTrace;
}
//釋放資源
public void release() {
Socket socket;
Connection releasedConnection;
synchronized (connectionPool) {
releasedConnection = connection;
socket = deallocate(false, true, false);//會關閉connection對象
if (connection != null) releasedConnection = null;
}
closeQuietly(socket);
if (releasedConnection != null) {
eventListener.connectionReleased(call, releasedConnection);
}
}
}
後續會針對StreamAllocation專門進行更深入的介紹。
重定向功能是否開啟,在建立OkHttpClient的時候可以設定:
new OkHttpClient().newBuilder()
.followRedirects(false) //禁制OkHttp的重定向操作,我們自己處理重定向
.followSslRedirects(false)//https的重定向也自己處理
簡單進行流程總結: