介紹
攔截器鏈,采用責任鏈模式,將一次事物的耦合度降低。
源碼分析
RealInterceptorChain
RealInterceptorChain就是個List<Interceptor>,源碼比較簡單,主要功能proceed
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
...
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1,request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
...
return response;
}
RetryAndFollowUpInterceptor
RetryAndFollowUpInterceptor的責任是失敗重試和重定向,主要功能在于
intercept
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//創建一個新的流
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
int followUpCount = 0;
//重定向可能產生多個Response
Response priorResponse = null;
//循環直到取消或者拋出exception
while (true) {
...
//輔助判斷是否要釋放連接
boolean releaseConnection = true;
try {
//取得下級返回的response
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (){
//各種異常捕捉處理
...
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
...
//重定向,根據返回response生成新的request
Request followUp = followUpRequest(response);
...
//判斷是否是sameConnection(host==host&&port==port&&scheme==scheme)可復用鏈路
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(followUp.url()), 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;
}
}
BridgeInterceptor
BridgeInterceptor可以理解成轉換器
Bridges from application code to network code. First it builds a network request from a user request. Then it proceeds to call the network. Finally it builds a user response from the network response.
源碼非常簡單,主要內容在于intercept
@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();
}
CacheInterceptor
緩存攔截器更具客戶端是否支持緩存和相關的緩存策略決定從網絡獲取或者從緩存獲取Response,主要內容在于intercept
public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
//根據緩存策略獲取緩存Request和Response
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
...
//緩存不可用或者緩存過期,網絡獲取
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
...
//更新緩存
return response;
}
ConnectInterceptor
ConnectInterceptor建立與服務器的連接
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
//獲取可復用流
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
//根據HTTP/1.x(keep-alive)和HTTP/2(流復用)的復用機制,發起連接
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
CallServerInterceptor
CallServerInterceptor和服務器交互數據
@Override public Response intercept(Chain chain) throws IOException {
...
long sentRequestMillis = System.currentTimeMillis();
//發送header數據
httpCodec.writeRequestHeaders(request);
Response.Builder responseBuilder = null;
//根據是否支持100-continue,發送body數據
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
...
}
httpCodec.finishRequest();
if (responseBuilder == null) {
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
//response處理
...
return response;
}
擴展
責任鏈中大體流程分析完,其中有很多可以深究的地方,包括緩存和多路復用的實現
緩存
Okhttp緩存涉及到internal cache(接口設計),cache(實現類),CacheStrategy(緩存策略),DiskLruCache(lru cache實現),具體可以參考BlackSwift寫的
OkHttp3源碼分析[緩存策略] ,OkHttp3源碼分析[DiskLruCache]
多路復用
多路復用設計到包括StreamAllocation(上層流),ConnectionPool(連接池), http1Codec,http2Codec。
首先要了解HTTP/1(keep-alive)和HTTP/2(二進制流)的相關知識,才能更好的理解OKhttp多路復用的實現,具體可以參考
OkHttp 3.7源碼分析(五)——連接池和
OkHttp3源碼分析[復用連接池]