CacheInterceptor緩存攔截器分析
源碼地址:https://github.com/square/okhttp
不知不覺來到了第三個(gè)攔截器,經(jīng)過前面的兩個(gè)攔截器:
RetryAndFollowUpInterceptor(初始化連接,重連);
BridgeInterceptor(頭處理,Gzip, cookie處理)洲守。
而這個(gè) CacheInterceptor,是處理緩存相關(guān)的攔截器。
緩存知識(shí)
對(duì)于http 緩存的相關(guān)知識(shí)葬毫,可以參考:
http://www.cnblogs.com/chenqf/p/6386163.html
這文章中比較詳細(xì)的講解了,http中的緩存機(jī)制屡穗,以及一些基本的原理贴捡。如果你對(duì)緩存已經(jīng)了解,可以忽略村砂。
緩存攔截器基本流程
- 讀取候選緩存烂斋;
- 創(chuàng)建緩存策略(根據(jù)頭信息,判斷強(qiáng)制緩存础废,對(duì)比緩存等策略)汛骂;
- 根據(jù)策略,不使用網(wǎng)絡(luò)评腺,緩存又沒有直接報(bào)錯(cuò)帘瞭;
- 根據(jù)策略,不使用網(wǎng)絡(luò)蒿讥,有緩存就直接返回图张;
- 前面?zhèn)€都沒有返回,讀取網(wǎng)絡(luò)結(jié)果(跑下一個(gè)攔截器)诈悍;
- 接收到的網(wǎng)絡(luò)結(jié)果祸轮,如果是code 304, 使用緩存,返回緩存結(jié)果(對(duì)比緩存)
- 讀取網(wǎng)絡(luò)結(jié)果侥钳;
- 對(duì)數(shù)據(jù)進(jìn)行緩存适袜;
- 返回網(wǎng)絡(luò)讀取的結(jié)果。
源碼解讀
@Override public Response intercept(Chain chain) throws IOException {
//1. 讀取候選緩存舷夺;
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
//2. 創(chuàng)建緩存策略(強(qiáng)制緩存苦酱,對(duì)比緩存等策略)售貌;
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
cache.trackResponse(strategy);
}
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
//根據(jù)策略,不使用網(wǎng)絡(luò)疫萤,緩存又沒有直接報(bào)錯(cuò)颂跨;
// If we're forbidden from using the network and the cache is insufficient, fail.
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();
}
// 4. 根據(jù)策略,不使用網(wǎng)絡(luò)扯饶,有緩存就直接返回恒削;
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
// 5. 前面?zhèn)€都沒有返回,讀取網(wǎng)絡(luò)結(jié)果(跑下一個(gè)攔截器)尾序;
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());
}
}
//6. 接收到的網(wǎng)絡(luò)結(jié)果钓丰,如果是code 304, 使用緩存,返回緩存結(jié)果(對(duì)比緩存)
// If we have a cache response too, then we're doing a conditional get.
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();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
//7. 讀取網(wǎng)絡(luò)結(jié)果每币;
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
//8. 對(duì)數(shù)據(jù)進(jìn)行緩存携丁;
if (HttpHeaders.hasBody(response)) {
CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
response = cacheWritingResponse(cacheRequest, response);
}
//9. 返回網(wǎng)絡(luò)讀取的結(jié)果。
return response;
}
總結(jié)
緩存實(shí)際上是一個(gè)比較復(fù)雜的邏輯兰怠,這里只是簡(jiǎn)單的對(duì)攔截器的功能進(jìn)行了分析梦鉴,并沒有深入的分析其中的實(shí)現(xiàn),但實(shí)際上緩存不屬于okhttp上的功能揭保,只是對(duì)http協(xié)議做了處理尚揣,整體其實(shí)是屬于http相關(guān)的,而在okhttp中使用了攔截器的方式掖举,進(jìn)行了實(shí)現(xiàn)快骗。
有興趣的可以繼續(xù)研究一下以下的點(diǎn):
- 緩存策略是如何生成的,做了些什么判斷
- 緩存是如何保存的
系列:
OKhttp源碼學(xué)習(xí)(一)—— 基本請(qǐng)求流程
OKhttp源碼學(xué)習(xí)(二)—— OkHttpClient
OKhttp源碼學(xué)習(xí)(三)—— Request, RealCall
OKhttp源碼學(xué)習(xí)(四)—— RetryAndFollowUpInterceptor攔截器
OKhttp源碼學(xué)習(xí)(五)—— BridgeInterceptor攔截器
OKhttp源碼學(xué)習(xí)(七)—— ConnectInterceptor攔截器
OKhttp源碼學(xué)習(xí)(八)——CallServerInterceptor攔截器
OKhttp源碼學(xué)習(xí)(九)—— 任務(wù)管理(Dispatcher)