1、okhttp源碼解析-整體流程
2、okhttp源碼解析-攔截器RetryAndFllowUpInterceptor
3、okhttp源碼解析-攔截器BridgeInterceptor
4、okhttp源碼解析-攔截器CacheInterceptor
5、okhttp源碼解析-攔截器ConnectInterceptor
6、okhttp源碼解析-攔截器CallServerInterceptor
7、okhttp源碼解析-Dispatcher任務(wù)管理器
一、http 緩存策略
1、強(qiáng)制緩存
2、對(duì)比緩存
1、強(qiáng)制緩存
對(duì)于強(qiáng)制緩存來(lái)說(shuō),響應(yīng)header中會(huì)有兩個(gè)字段來(lái)標(biāo)明失效規(guī)則(Expires/Cache-Control)
Expires
Expires的值為服務(wù)端返回的到期時(shí)間,即下一次請(qǐng)求時(shí),請(qǐng)求時(shí)間小于服務(wù)端返回的到期時(shí)間,直接使用緩存數(shù)據(jù)。
不過(guò)Expires 是HTTP 1.0的東西,現(xiàn)在默認(rèn)瀏覽器均默認(rèn)使用HTTP 1.1,所以它的作用基本忽略。
另一個(gè)問(wèn)題是,到期時(shí)間是由服務(wù)端生成的,但是客戶端時(shí)間可能跟服務(wù)端時(shí)間有誤差,這就會(huì)導(dǎo)致緩存命中的誤差。
所以HTTP 1.1 的版本,使用Cache-Control替代。-
Cache-Control
- Cache-Control 是最重要的規(guī)則。常見(jiàn)的取值有private、public、no-cache、max-age,no-store,默認(rèn)為private。
| 序號(hào) | 取值 | 描述 |
|---|---|---|
| 1 | private | 客戶端可以緩存。 |
| 2 | public | 客戶端和代理服務(wù)器都可緩存(前端的同學(xué),可以認(rèn)為public和private是一樣的)。 |
| 3 | max-age=xxx | 配合強(qiáng)制緩存使用,緩存的內(nèi)容將在 xxx 秒后失效。 |
| 4 | no-cache | 配合對(duì)比緩存使用,需要使用對(duì)比緩存來(lái)驗(yàn)證緩存數(shù)據(jù)(需要配合其他header處理,下面是詳情)。 |
| 5 | no-store | 不使用緩存,所有內(nèi)容都不會(huì)緩存,強(qiáng)制緩存,對(duì)比緩存都不會(huì)觸發(fā)。 |
舉個(gè)例子:

圖中Cache-Control僅指定了max-age,所以默認(rèn)為private,緩存時(shí)間為31536000秒(365天)
也就是說(shuō),在365天內(nèi)再次請(qǐng)求這條數(shù)據(jù),都會(huì)直接獲取緩存數(shù)據(jù)庫(kù)中的數(shù)據(jù),直接使用。
2、對(duì)比緩存
對(duì)比緩存有兩種形式的標(biāo)識(shí)可用。
- 1、Last-Modified / If-Modified-Since
- 2、Etag / If-None-Match 這組的優(yōu)先級(jí)大于上一組
2.1、Last-Modified / If-Modified-Since
Last-Modified:
服務(wù)器在第一次響應(yīng)時(shí),攜帶在Response的Header中,告訴瀏覽器資源的最后修改時(shí)間。If-Modified-Since:
客戶端發(fā)送第二請(qǐng)求時(shí),攜帶在Request的Header中,值就是last-modified的值。服務(wù)端在第二次收到If-Modified-Since時(shí),會(huì)比較當(dāng)前資源變更的time與if-modified-since時(shí)間的先后,判斷瀏覽器是否可以使用緩存數(shù)據(jù)。如果可以使用緩存就返回304狀態(tài)碼。
2.2、Etag / If-None-Match (優(yōu)先級(jí)大于上一組)
-
Etag
同樣是,服務(wù)端在第一次響應(yīng)時(shí),攜帶在Response的Header中。其中攜帶的信息是資源的唯一表示,可以理解成hash值。 -
If-None-Match
客戶端發(fā)送第二次請(qǐng)求時(shí),攜帶在Request的Header中。攜帶值就是第一次服務(wù)器傳回的Etag值 - 服務(wù)器在接收到If-None-Match時(shí),會(huì)和當(dāng)前資源的唯一值做比較。值相等則說(shuō)明緩存可用,返回304狀態(tài)碼
二、CacheInterceptor 流程圖
上邊已經(jīng)對(duì)http的緩存策略有了一些了解,那接下來(lái)的攔截器也就清晰了。它其實(shí)就是根據(jù)http的緩存策略,進(jìn)行緩存的處理。
- 1、獲取本地的緩存數(shù)據(jù)cacheResponse
- 2、通過(guò)CacheStrategy類,判斷是否滿足強(qiáng)制緩存規(guī)則
- 2.1、若滿足返回Request為null,則不需要進(jìn)行網(wǎng)絡(luò)請(qǐng)求,直接返回緩存數(shù)據(jù)。
- 2.2、若不滿足,則根據(jù)cacheResponse進(jìn)行Request的Header頭If-Modified-Since或者If-None-Match參數(shù)封裝。
- 3、通過(guò)攔截器責(zé)任鏈,獲取網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)networkResponse。chains.process()
- 4、通過(guò)判斷網(wǎng)絡(luò)networkResponse,和本地cacheResponse數(shù)據(jù),來(lái)判斷是否使用緩存。這個(gè)過(guò)程就是對(duì)比緩存規(guī)則
- 4.1 判斷是否使用Etag/If-None-Match規(guī)則,通過(guò)networkResponse的狀態(tài)碼判斷是否為304,304即使用緩存。
- 4.2 判斷是否使用Last-Modified/If-Modifed-Since規(guī)則,通過(guò)判斷cacheResponse和netwrokResponse的Last-Modified的值,來(lái)判斷是否使用緩存。
- 5、判斷此次網(wǎng)絡(luò)請(qǐng)求的code、Method和Cache-control的值,來(lái)決定是否緩存本次數(shù)據(jù)?
- 5.1 如果本次請(qǐng)求code是200、203、204等等時(shí),并且cache-control的值不是no-store則說(shuō)明可以對(duì)此次數(shù)據(jù)進(jìn)行緩存。
- 5.2 如果不滿足上述條件,則此次數(shù)據(jù)不緩存,并且要清除歷史記錄。

三、源碼解析
public Response intercept(Interceptor.Chain chain) throws IOException {
//1、根據(jù)Request信息,獲取本地緩存的Response信息。
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
//2、根據(jù)緩存策略,
// 2.1處理請(qǐng)求Request信息,
// 根據(jù)緩存的cacheResponse的Header中是否存在Etag/Last-Modified參數(shù),
// 在Request的header中添加If-None-Match/If-Modified-Since等參數(shù)。
// 2.2獲取緩存中的cacheResponse
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.
}
// If we're forbidden from using the network and the cache is insufficient, fail.
// 3、如果網(wǎng)絡(luò)請(qǐng)求失敗,而且沒(méi)有緩存信息,直接拋504錯(cuò)誤碼。
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(EMPTY_BODY)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// If we don't need the network, we're done.
// 4、如果Request經(jīng)過(guò)CacheStrategy策略處理后,是null,那么代表此次請(qǐng)求直接走緩存。一般情況就是命中了強(qiáng)制緩存規(guī)則。
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
// 5、如果走到這,則說(shuō)明需要發(fā)送網(wǎng)絡(luò)請(qǐng)求??赡苁菦](méi)有緩存,也可能是命中對(duì)比緩存規(guī)則。
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());
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
//6、如果緩存cacheResponse不為空,則根據(jù)networkResponse返回的狀態(tài)碼,返回true則使用緩存。
// 6.1、304則使用緩存。這種情況說(shuō)明使用的Etag/If-None-Match規(guī)則,服務(wù)器直接判斷是否使用緩存,然后返回狀態(tài)碼。
// 6.2、兩次Last-Modified對(duì)比之后,發(fā)現(xiàn)資源更新時(shí)間沒(méi)有過(guò)期,則使用緩存??蛻舳藖?lái)判斷。
if (validate(cacheResponse, networkResponse)) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.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());
}
}
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (HttpHeaders.hasBody(response)) {
// 7、根據(jù)Request信息,判斷Http請(qǐng)求是否允許使用緩存。
CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
// 8、根據(jù)cacheRequest,存儲(chǔ)本次請(qǐng)求的緩存。
response = cacheWritingResponse(cacheRequest, response);
}
return response;
}
2、validate() 判斷對(duì)比緩存,是否使用緩存數(shù)據(jù)。
/**
* Returns true if {@code cached} should be used; false if {@code network} response should be
* used.
*/
private static boolean validate(Response cached, Response network) {
//1、如果http返回碼是304,則說(shuō)明使用Etag/If-None-Match規(guī)則,直接由服務(wù)端判斷完,返回給客戶端。304則代表使用緩存。
if (network.code() == HTTP_NOT_MODIFIED) return true;
// 2、這種則是判斷,Last-Modified/If-Modified-Since規(guī)則。通過(guò)判斷兩次Response的Last-Modified的時(shí)間,來(lái)確定是否使用緩存。
Date lastModified = cached.headers().getDate("Last-Modified");
if (lastModified != null) {
Date networkLastModified = network.headers().getDate("Last-Modified");
if (networkLastModified != null
&& networkLastModified.getTime() < lastModified.getTime()) {
return true;
}
}
return false;
}
3、maybeCache() 判斷當(dāng)前http請(qǐng)求是否允許我們使用緩存。允許則進(jìn)行緩存,不允許則返回null。
private CacheRequest maybeCache(Response userResponse, Request networkRequest,
InternalCache responseCache) throws IOException {
//1.如果緩存工具類為空,則不能進(jìn)行緩存,則返回null
if (responseCache == null) return null;
// Should we cache this response for this request?
// 2.根據(jù)http狀態(tài)碼,以及cache-control的值來(lái)判斷是否可以進(jìn)行緩存。例:no-store情況下,不能進(jìn)行緩存。
// 這里有個(gè)!判斷,所以如果進(jìn)入這個(gè)if,則說(shuō)明不允許進(jìn)行緩存。
if (!CacheStrategy.isCacheable(userResponse, networkRequest)) {
//3.這次請(qǐng)求不允許緩存,但是有可能存在之前的緩存記錄。所以要清除歷史記錄。
//由于只有"POST"、"PATCH"、"PUT"、"DELETE"、"MOVE"的Method可以緩存,所以清除記錄時(shí),也只需要請(qǐng)求這幾種狀態(tài)時(shí)的緩存。
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
//清除緩存
responseCache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
return null;
}
// Offer this request to the cache.
// 判斷如果走到這個(gè),說(shuō)明需要緩存,則進(jìn)行緩存存儲(chǔ)
return responseCache.put(userResponse);
}
//根據(jù)http狀態(tài)碼、cache-control值判斷此次http請(qǐng)求是否允許緩存。
public static boolean isCacheable(Response response, Request request) {
switch (response.code()) {
//以下這些狀態(tài)嗎,才可使用緩存。但是break之后,仍需判斷cache-control的值。
case HTTP_OK:
case HTTP_NOT_AUTHORITATIVE:
case HTTP_NO_CONTENT:
case HTTP_MULT_CHOICE:
case HTTP_MOVED_PERM:
case HTTP_NOT_FOUND:
case HTTP_BAD_METHOD:
case HTTP_GONE:
case HTTP_REQ_TOO_LONG:
case HTTP_NOT_IMPLEMENTED:
case StatusLine.HTTP_PERM_REDIRECT:
break;
case HTTP_MOVED_TEMP:
case StatusLine.HTTP_TEMP_REDIRECT:
if (response.header("Expires") != null
|| response.cacheControl().maxAgeSeconds() != -1
|| response.cacheControl().isPublic()
|| response.cacheControl().isPrivate()) {
break;
}
//其他狀態(tài)碼,直接不能使用緩存。
default:
return false;
}
// Request、Response的Cache-Control值都不是no-store時(shí)才被允許使用緩存。
return !response.cacheControl().noStore() && !request.cacheControl().noStore();
}
//只有這些http method才能使用緩存
public static boolean invalidatesCache(String method) {
return method.equals("POST")
|| method.equals("PATCH")
|| method.equals("PUT")
|| method.equals("DELETE")
|| method.equals("MOVE"); // WebDAV
}