OkHttp網絡請求框架源碼解析二
本篇講解OkHttp核心知識攔截器,共有5個攔截器,分別爲RetryAndFollowUpInterceptor,BridgeInterceptor, CacheInterceptor,ConnectInterceptor 和 CallServerInterceptor。下面一一講到。
//上一篇講到,作爲Runnable的 AsyncCall,最終的execute()方法爲RealCall裏面的execute()方法。
@Override protected void execute() {
//獲取響應回來的數據response
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
//回調onFailure
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
//回調onResponse
responseCallback.onResponse(RealCall.this, response);
}
......
//線程池執行完線程任務後,調用此方法.
client.dispatcher().finished(this);
}
//不管是同步請求,還是異步請求,都是通過getResponseWithInterceptorChain方法獲取響應。
Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList<>();
//添加用戶自定義的攔截器
interceptors.addAll(client.interceptors());
......
//創建攔截器鏈
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
//創建下一個攔截器鏈
return chain.proceed(originalRequest);
}
//chain.proceed()方法實現
public Response proceed() throws IOException {
......
//創建下一個攔截器鏈 index+1
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
return response;
}
第一個攔截器,重試重定向攔截器:RetryAndFollowUpInterceptor。
1.創建StreamAllocation對象;
2.調用RealInterceptorChain.proceed()方法進行網絡請求;
3.根據結果判斷是否重新請求;
4.調用下一個攔截器;
//RetryAndFollowUpInterceptor攔截器裏面的核心方法intercept(),
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//StreamAllocation 獲取建立Http連接的所有網絡組件
//雖然在這裏被創建,但是這裏沒有使用。
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
......
//重試次數20次
if (++followUpCount > 20) {
streamAllocation.release();
}
}
第二個攔截器,橋接攔截器,BridgeInterceptor。
1.將request請求轉換成可以進行網絡訪問的請求;
2.進行網絡請求;
3.將響應response轉換成客戶度可以用的response。
//BridgeInterceptor橋接攔截器的核心方法intercept()
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
給Request添加各種頭部信息
requestBuilder.header("Content-Type", contentType.toString());
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
}
}
......
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
requestBuilder.header("Connection", "Keep-Alive");
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
requestBuilder.header("Cookie", cookieHeader(cookies));
requestBuilder.header("User-Agent", Version.userAgent());
//從服務器獲取response
Response networkResponse = chain.proceed(requestBuilder.build());
//將服務器返回的數據頭部轉換成客戶端可以接收的形式
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(),
networkResponse.headers());
第三個攔截器,緩存攔截器CacheInterceptor。
//CacheInterceptor核心方法intercept
@Override public Response intercept(Chain chain) throws IOException {
//獲取緩存
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
//創建CacheStrategy
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(),
cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
//關於緩存命中率,如果使用了緩存,則hitCount++。
cache.trackResponse(strategy);
//第一種情況,如果不能使用網絡,並且沒有緩存,則OkHttp內部構建一個504響應作爲response。
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
//第二種情況,不能使用網絡,但是有緩存,則使用緩存作爲response。
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
//第三種情況,可以使用網絡,不管是否有緩存都會調用chain.proceed方法進行網絡請求。
Response networkResponse = null;
networkResponse = chain.proceed(networkRequest);
//如果有緩存,並且服務器響應碼爲304,表示內容未被修改,則使用緩存作爲response.
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
return response;
}
}
//如果沒有緩存,則將剛纔網絡請求的response作爲response.
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
//如果代碼能走到這裏,說明前面網絡請求的響應碼不是304,表示response被修改了,則更新緩存。
if (cache != null) {
//條件判斷
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
//存入新的緩存
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
//假如請求方式不是get,則刪除緩存,因爲OkHttp只對get方式的請求進行緩存。
if (HttpMethod.invalidatesCache(networkRequest.method())) {
cache.remove(networkRequest);
}
}
return response;
}
第四個攔截器,連接池攔截器,ConnectInterceptor。
//ConnectInterceptor整個類的代碼
public final class ConnectInterceptor implements Interceptor {
public final OkHttpClient client;
public ConnectInterceptor(OkHttpClient client) {
this.client = client;
}
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
//StreamAllocation用來獲取網絡請求需要的所有組件
StreamAllocation streamAllocation = realChain.streamAllocation();
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//HttpCodec用來 編碼request,解碼response
HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
//RealConnection 進行實際的網絡io傳輸
RealConnection connection = streamAllocation.connection();
//將重要的HttpCodec對象和RealConnection對象傳遞給後面的攔截器
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
}
//streamAllocation.newStream方法的實現
public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
//通過findHealthyConnection方法獲取RealConnection
RealConnection resultConnection = findHealthyConnection(connectTimeout,
readTimeout,writeTimeout,connectionRetryEnabled, doExtensiveHealthChecks);
//通過RealConnection的newCodec方法獲取HttpCodec對象,最後返回。
HttpCodec resultCodec = resultConnection.newCodec(client, this);
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
}
//findHealthyConnection方法的實現
private RealConnection findHealthyConnection(){
while (true) {
//循環裏面,通過findConnection()方法獲取RealConnection對象。然後返回。
RealConnection candidate = findConnection(connectTimeout, readTimeout,
writeTimeout,
connectionRetryEnabled);
synchronized (connectionPool) {
if (candidate.successCount == 0) {
return candidate;
}
}
......
}
//findConnection()方法的實現
private RealConnection findConnection(){
//先複用已有的連接,如果滿足條件,則返回
RealConnection allocatedConnection = this.connection;
if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
return allocatedConnection;
}
//如果不能複用,則從連接池connectionPool裏面獲取RealConnection對象。
Internal.instance.get(connectionPool, address, this, null);
if (connection != null) {
return connection;
}
//調用RealConnection對象的connect()方法進行實際的網絡請求連接
result.connect(connectTimeout, readTimeout, writeTimeout,
connectionRetryEnabled);
//然後把上面的連接放入連接池connectionPool
Internal.instance.put(connectionPool, result);
}
第五個攔截器,CallServerInterceptor。
//CallInterceptor攔截器的核心方法實現
@Override public Response intercept(Chain chain) throws IOException {
//5個重要的對象,第一個:攔截器鏈
RealInterceptorChain realChain = (RealInterceptorChain) chain;
//5個重要的對象,第二個:HttpCodec編碼request,解碼response
HttpCodec httpCodec = realChain.httpStream();
//5個重要的對象,第三個:分配網絡請求組件
StreamAllocation streamAllocation = realChain.streamAllocation();
//5個重要的對象,第四個:真正的網絡請求
RealConnection connection = (RealConnection) realChain.connection();
//5個重要的對象,第五個:
Request request = realChain.request();
//第一步:寫請求頭。
httpCodec.writeRequestHeaders(request);
//第二步:寫請求體
request.body().writeTo(bufferedRequestBody);
//第三步:結束請求
httpCodec.finishRequest();
//第四步:讀取response請求頭
responseBuilder = httpCodec.readResponseHeaders(false);
//第五步:讀取response請求體
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}