1.引言
我們在前面幾個章節的學習過程中,已經可以明白OkHttp的基本使用,OkHttp在使用的過程中幾個常用的類的分析,OkHttp的配置設定器Dispatcher。其中在RealCall中,不管是同步請求還是異步請求,最終都會調用的一個方法,得到網絡請求響應結果Response,就是getResponseWithInterceptorChain()方法。因為這個方法用的是攔截器鍊調用攔截器執行對應的方法,是以我們決定放到現在來講,我們先看一下getResponseWithInterceptorChain()的調用位置:
//同步請求擷取請求結果
@Override public Response execute() throws IOException {
...
try {
...
Response result = getResponseWithInterceptorChain();//擷取請求結果
...
return result;
} catch (IOException e) {
...
} finally {
...
}
}
上面我們看到的,是我們調用Call的同步請求方法execute()時,的部分代碼,可以看到,在該方法中真正擷取請求結果的隻有一句代碼Response result = getResponseWithInterceptorChain(),就是說,同步請求會直接調用getResponseWithInterceptorChain()方法,擷取Response并傳回個調用者。
我們再看異步請求是如何調用的getResponseWithInterceptorChain()方法,首先我們異步請求調用的是Call的enqueue(Callback)方法:
//Call的異步請求方法,請求結果會利用Callback接口回調傳回給調用者
@Override public void enqueue(Callback responseCallback) {
...
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
通過部分代碼,我們看到,Call的enqueue(Callback)方法,會調用Dispatcher的enqueue(AsyncCall)方法,我們繼續看一下Dispatcher的enqueue(AsyncCall)方法:
//Dispatcher的異步請求方法,被Call調用。Asyncall是異步請求任務
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);//線程池執行異步任務,最終會執行AsyncCall的execute()方法
} else {
readyAsyncCalls.add(call);
}
}
上面的代碼我們前面已經講過多次,executorService().execute(call)使用線程池異步執行了異步請求任務,其中的請求任務代碼,在AsyncCall類的execute()方法中,相當于在異步線程中執行了AsyncCall類的execute()方法,是以我們在跳轉到AsyncCall類中,檢視一下它的execute()方法:
//RealCall的内部類AsyncCall,用來做異步請求任務,是Runnable的子類。
@Override protected void execute() {
...
try {
//擷取請求結果
Response response = getResponseWithInterceptorChain();
if (...) {
...//判斷,使用Callback接口回調傳回給調用者
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
...//判斷,使用Callback接口回調傳回給調用者
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
...
} else {
...//判斷,使用Callback接口回調傳回給調用者
responseCallback.onFailure(RealCall.this, e);
}
} finally {
...
}
}
上面代碼,相信大家都能看懂,在AsyncCall的execute()方法中調用了getResponseWithInterceptorChain()方法。擷取到了請求結果,然後經過判斷,在利用調用者傳進來的Callback接口,做接口回調,将結果回傳給調用者,完成了異步請求。
2.getResponseWithInterceptorChain()方法
前面說了這麼多,就是為了說明不管是同步請求還是異步請求,都會調用RealCall中的getResponseWithInterceptorChain()來擷取請求結果Response,是以我們現在将getResponseWithInterceptorChain()代碼放上來,分析一下:
//RealCall的同步、異步請求都會調用此方法,這是擷取網絡請求結果Response的關鍵方法
Response getResponseWithInterceptorChain() throws IOException {
//建立一個存儲攔截器的集合
List<Interceptor> interceptors = new ArrayList<>();
//将使用者傳入的Application攔截器添加到集合中
interceptors.addAll(client.interceptors());
//将重定向攔截器添加到集合中
interceptors.add(retryAndFollowUpInterceptor);
//将橋接攔截器加入到集合中
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//将緩存攔截器加入到集合中
interceptors.add(new CacheInterceptor(client.internalCache()));
//将連接配接攔截器加入到集合中
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {//如果不是WebSocket連接配接
//将使用者傳入的Network攔截器加入的
interceptors.addAll(client.networkInterceptors());
}
//将CallServer攔截器(與伺服器進行資料互動)加入到集合中
interceptors.add(new CallServerInterceptor(forWebSocket));
//建立攔截器鍊,index初始值為0
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
//調用攔截器鍊的proceed方法,就會調用到所有攔截器,并傳回請求結果Response
return chain.proceed(originalRequest);
}
這一節,我們先不講代碼中提到的每個攔截器的作用,我們隻講攔截器的調用。我們從上面代碼中可以看到,首先會将各種攔截器按照先後順序,分别加入到新建立的ArryList集合中,然後建立了Interceptor.Chain攔截器鍊執行個體,在建立過程中,構造函數傳入了存有各種攔截器的集合interceptors、數字0、使用者建立好的Request對象、目前RealCall對象、OkHttp網絡請求詳細事件回調接口、逾時設定。最後,調用了攔截器鍊chain的proceed(Request)方法,該方法傳回Response正是調用者需要的伺服器端的響應結果。我們先大概知道一下,這個方法中,主要做了些什麼工作。接下來我們來看攔截器鍊。
3.Interceptor.Chain,攔截器鍊
首先我們通過代碼查找,看到Interceptor.Chain是一個interface,它的目前唯一實作類就是RealInterceptorChain,我們看下這個類的構造方法
//RealInterceptorChain構造方法
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
this.interceptors = interceptors;//RealCall那邊建立好并且已經添加好了所有攔截器
this.connection = connection;//null
this.streamAllocation = streamAllocation;//null
this.httpCodec = httpCodec;//null
this.index = index;//RealCall傳入的是0
this.request = request;//使用者建構好的Request
this.call = call;//目前RealCall
this.eventListener = eventListener;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
this.writeTimeout = writeTimeout;
}
我們需要注意一點,他的變量index正是我們剛才RealCall的getResponseWithInterceptorChain()方法中傳入的0,我們接着看一下我們前面調用的RealInterceptorChain的proceed(Request)方法:
//首次調用攔截器鍊
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
//繼續調用攔截器鍊,将擷取到的Response傳回去
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
...
//這裡需要注意,建立的攔截器鍊RealInterceptorChain,傳入的參數中(index+1)
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//這裡需要注意,擷取到的攔截器是角标為index的攔截器,而index正是通過其構造函數指派的
Interceptor interceptor = interceptors.get(index);
//調用攔截器的intercept()方法擷取Response
Response response = interceptor.intercept(next);
...
return response;//将請求結果傳回
}
講到這裡,可能很多人還是有些懵懵懂懂,我來幫大家捋一下:
1、在RealCall的getResponseWithInterceptorChain()方法中,我們第一次建立了攔截器鍊對象RealInterceptorChain,将存有攔截器的集合interceptors傳給了RealInterceptorChain,同時設定了它的index為0,緊接着調用了RealInterceptorChain的proceed方法;
2、在RealInterceptorChain的proceed方法中,再次建立了RealInterceptorChain的執行個體,繼續将存有攔截器的集合interceptors傳遞給新建立的RealInterceptorChain,這時給他設定的index為目前index+1,然後從interceptors中擷取角标為目前index的Interceptor,執行Interceptor的intercept(Chain)方法,參數Chain就是新建立的index+1的RealInterceptorChain對象,并将擷取到的Response傳回給上一個調用者;
3、Interceptor本身是interface,其intercept(Chain)方法具體看它的實作類,可以肯定的一點是,intercept(Chain)方法中,除了做攔截器本身該做的一些操作外,會繼續調用Chain的proceed方法,進入Chain的proceed方法中繼續建立index+1的Chain,并且擷取角标為index的Interceptor,調用它的intercept(Chain)方法......一直調用下去,直到最後一個攔截器為止。
上面我說捋一下,我想還有人不太明白,因為到現在為止,還缺少一部分代碼,那就是Interceptor的intercept(Chain)方法究竟是怎麼樣子的,我們先看下Interceptor接口的類繼承情況

其中LoggingInterceptor是我自己建立的繼承自Interceptor,其他五個都是OkHttp架構已經提供的攔截器,我們在剛才講getResponseWithInterceptorChain()方法時候,都見過的,先不糾結每一個攔截器的作用。我們主要是為了研究一下這些攔截器的intercept(Chain)方法。getResponseWithInterceptorChain()方法建立了存儲攔截器的ArrayList集合,并且将攔截器都存入了該集合,我們剛才提到,會根據順序,每次index+1來擷取interceptors中的Interceptor,并調用其intercept(Chain)方法。我們取兩個具有代表性的進行研究,這五個攔截器中,按照添加順序排在最前面的RetryAndFollowUpInterceptor和排在最後的CallServerInterceptor,分别看一下他們的intercept(Chain)方法
RetryAndFollowUpInterceptor的intercept(Chain)方法
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
...
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
...
while (true) {
...//此處調用了攔截器鍊RealInterceptorChain的proceed方法
response = realChain.proceed(request, streamAllocation, null, null);
...
return response;
...
}
}
我們可以發現,在RetryAndFollowUpInterceptor的intercept(Chain)方法中,的确調用了攔截器鍊RealInterceptorChain的proceed()方法,這樣就可以繼續往下,調用下一個Interceptor的intercept(Chain)方法,一直調用下去,直到最後一個攔截器CallServerInterceptor結束。
CallServerInterceptor的intercept(Chain)
//這是CallServerInterceptor的intercept(Chain)方法,該方法不會調用Chain的proceed方法
@Override public Response intercept(Chain chain) throws IOException {
...
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
...
return response;
}
通過仔細閱讀該代碼,發現CallServerInterceptor果然沒有繼續調用Chain的proceed方法。攔截器鍊Chain的proceed方法,第一次調用,是被RealCall類的getResponseWithInterceptorChain()方法,然後proceed擷取角标為index的Interceptor,調用Interceptor的intercept(Chain)方法,index+1,intercept(Chain)方法繼續調用下一個RealInterceptorChain的proceed方法,一直這樣調用下去,直到最後一個攔截器,這就是攔截器鍊的調用。
4.總結
我們先通過兩幅幅圖來描述攔截器鍊的工作
我們來簡單的總結一下本章要跟大家分享的:
1、OkHttp的網絡請求,其本質上是由攔截器鍊的調用完成的;
2、攔截器鍊的調用,起始于RealCall的getResponseWithInterceptorChain方法,結束與最後一個攔截器CallServerInterceptor的intercept(Chain)方法;
3、連接配接器鍊的調用,RealInterceptorChain的作用是按照順序依次調用每一個攔截器的intercept(Chain)方法。攔截器Interceptor的作用是發揮自身攔截器的特性,對Request和Response做出操作,調用RealInterceptorChain的proceed()方法,讓其繼續下去;
4、我們可以将攔截器的調用看作遞歸,逐層向下調用,直到最後一個攔截器傳回Response,然後在逐層向上,一邊處理Response一邊傳回給上一層,最終傳回給調用。
本章節關于攔截器鍊的講解到此結束了,攔截器是OkHttp架構的精髓,是以我們會拿出幾篇文章來詳細分析,下一章節開始我們将分析每一個攔截器。