介紹完緩存以後,如今開始介紹緩存攔截器CacheInterceptor了,一樣也是查看其intercept()方法,這裏邊上片斷代碼邊解析,化整爲零:緩存
@Override public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
...
}
複製代碼
首先經過判斷緩存對象是否爲null,若是不爲null則根據傳入的Chain對象的request獲取緩存的Response。bash
@Override public Response intercept(Chain chain) throws IOException {
...
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
...
}
複製代碼
建立緩存策略對象CacheStrategy,CacheStrategy內部維護了一個Request和一個Response,該類主要是用於解決到底使用網絡仍是緩存,亦或是二者皆用:服務器
/** 若是不使用網絡,則 networkRequest爲 null */
public final @Nullable Request networkRequest;
/** 若是不使用緩存,則 cacheResponse爲 null */
public final @Nullable Response cacheResponse;
複製代碼
關於緩存策略的建立,咱們查看CacheStrategy的內部類Factory的get()方法:網絡
public CacheStrategy get() {
CacheStrategy candidate = getCandidate();
if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
// We're forbidden from using the network and the cache is insufficient. return new CacheStrategy(null, null); } return candidate; } 複製代碼
該方法比較簡單,建立CacheStrategy的主要的邏輯是在getCandidate()方法中:ide
private CacheStrategy getCandidate() {
//找不到緩存,須要網絡請求
if (cacheResponse == null) {
return new CacheStrategy(request, null);
}
// 若是是https請求且缺乏握手操做,須要網絡請求
if (request.isHttps() && cacheResponse.handshake() == null) {
return new CacheStrategy(request, null);
}
// 判斷網絡請求該不應緩存下來,不應緩存則,須要網絡請求
if (!isCacheable(cacheResponse, request)) {
return new CacheStrategy(request, null);
}
// 若是指定不緩存或者是可選擇的請求,須要網絡請求
CacheControl requestCaching = request.cacheControl();
if (requestCaching.noCache() || hasConditions(request)) {
return new CacheStrategy(request, null);
}
//上述須要網絡請求返回的 CacheStrategy 第一個參數傳入request
//若是緩存是不受影響的,CacheStrategy傳入cacheResponse
CacheControl responseCaching = cacheResponse.cacheControl();
if (responseCaching.immutable()) {
return new CacheStrategy(null, cacheResponse);
}
//能夠緩存,添加請求頭信息
if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
Response.Builder builder = cacheResponse.newBuilder();
if (ageMillis + minFreshMillis >= freshMillis) {
builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
}
long oneDayMillis = 24 * 60 * 60 * 1000L;
if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
}
return new CacheStrategy(null, builder.build());
}
...
}
複製代碼
介紹完CacheStrategy對象和建立CacheStrategy的過程後,接着分析intercept()方法:源碼分析
@Override public Response intercept(Chain chain) throws IOException {
...
//若是有緩存,更新統計指標, 增長命中率
if (cache != null) {
cache.trackResponse(strategy);
}
...
//若是當前沒有網絡且找不到緩存
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();
}
...
}
複製代碼
若是有緩存,經過調用CacheStrategy的trackResponse()方法更新統計指標, 更新網絡請求數和命中緩存數;post
接着經過判斷CacheStrategy的networkRequest和cacheResponse,若是兩者同時爲null,即當前沒有網絡且沒有緩存,則構造一個Response對象並返回,其中狀態碼爲504,並設置了提示的message信息。ui
接着分析:spa
@Override public Response intercept(Chain chain) throws IOException {
...
//若是不使用網絡請求,直接返回緩存的Response
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
//調用下一個攔截器進行處理
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());
}
}
//本地有緩存
if (cacheResponse != null) {
//服務器返回狀態碼爲HTTP_NOT_MODIFIED(304)
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
//使用緩存數據
Response response = cacheResponse.newBuilder()
...
.build();
...
} else {
closeQuietly(cacheResponse.body());
}
}
//若是服務器資源已經修改,使用網絡響應返回的最新數據
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
//若是有緩存
if (cache != null) {
//htpp 頭部有響應體且須要緩存
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
//請求方法不符合可以緩存
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
//移除對應的request
cache.remove(networkRequest);
} catch (IOException ignored) {
}
}
}
return response;
}
複製代碼
當服務器返回狀態碼爲HTTP_NOT_MODIFIED即304時,說明緩存還沒過時或服務器資源沒修改,此時返回緩存;若是服務器資源修改了,則使用網絡響應返回的最新數據構造Response,接着將最新的數據緩存並移除無效的緩存。.net
總結CacheInterceptor主要作的操做:
下一篇將講解五大攔截器中的最後兩個攔截器, ConnectInterceptor和CallServerInterceptor,感興趣的朋友能夠繼續閱讀:
OkHttpClient源碼分析(五)—— ConnectInterceptor和CallServerInterceptor