Serves requests from the cache and writes responses to the cache.
缓存拦截器,负责读取缓存直接返回、更新缓存。当网络请求有符合要求的Cache时,直接返回Cache。如果当前Cache失效,则删除。CacheStrategy:缓存策略,CacheStrategy类是一个非常重要的类,用于控制请求是网络获取还是缓存获取
@Override public Response intercept(Chain chain) throws IOException { // 得到 request 对应缓存中的 response Response cacheCandidate = cache != null ? cache.get(chain.request()) : null; // 获取当前时间,会和之前缓存的时间进行比较 long now = System.currentTimeMillis(); // 得到缓存策略 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. // 禁止网络并且没有缓存的话,返回失败 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(); } // If we don't need the network, we're done. //不用网络请求,返回缓存 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 we have a cache response too, then we are doing a conditional get. // 如果我们同时有缓存和 networkResponse ,根据情况使用 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()); } } Response response = networkResponse.newBuilder() .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build(); // 保存之前未缓存的缓存 if (cache != null) { if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) { // Offer this request to the cache. CacheRequest cacheRequest = cache.put(response); return cacheWritingResponse(cacheRequest, response); } if (HttpMethod.invalidatesCache(networkRequest.method())) { try { cache.remove(networkRequest); } catch (IOException ignored) { // The cache cannot be written. } } } return response; } 复制代码
networkRequest | cacheResponse | 结 果 |
---|---|---|
null | null | 禁止进行网络请求,但缓存不存在或者过期,只能返回503错误 |
null | non-null | 缓存可以使用,直接返回缓存,不用请求网络 |
non-null | null | 缓存不存在或者过期,直接访问网络 |
non-null | non-null | 条件get,请求网络 |
对于缓存拦截器来说,最重要的地方在于缓存策略的生成逻辑:
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); Request networkRequest = strategy.networkRequest; Response cacheResponse = strategy.cacheResponse; 复制代码
根据now(此刻时间),chain.request()(请求),cacheCandidate(缓存),来生成相应的请求和响应。下面我们来看一下缓存策略的生成逻辑:
public Factory(long nowMillis, Request request, Response cacheResponse) { this.nowMillis = nowMillis; this.request = request; this.cacheResponse = cacheResponse; if (cacheResponse != null) { this.sentRequestMillis = cacheResponse.sentRequestAtMillis(); this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis(); Headers headers = cacheResponse.headers(); for (int i = 0, size = headers.size(); i < size; i++) { String fieldName = headers.name(i); String value = headers.value(i); //日期 if ("Date".equalsIgnoreCase(fieldName)) { servedDate = HttpDate.parse(value); servedDateString = value; //有效期 } else if ("Expires".equalsIgnoreCase(fieldName)) { expires = HttpDate.parse(value); //上次修改 } else if ("Last-Modified".equalsIgnoreCase(fieldName)) { lastModified = HttpDate.parse(value); lastModifiedString = value; //响应对象在代理缓存中存在的时间,以秒为单位 } else if ("ETag".equalsIgnoreCase(fieldName)) { etag = value; //响应对象在代理缓存中存在的时间,以秒为单位 } else if ("Age".equalsIgnoreCase(fieldName)) { ageSeconds = HttpHeaders.parseSeconds(value, -1); } } } } 复制代码
Factory():Factory()方法记录一些数据:现在的时间、请求体、缓存响应。如果缓存响应有数据,则记录缓存的请求发送时间、收到响应的时间、服务器时间、有效期、上次修改、存活时间等。
/** * Returns a strategy to satisfy {@code request} using the a cached response {@code response}. */ public CacheStrategy get() { CacheStrategy candidate = getCandidate(); if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) { // We are forbidden from using the network and the cache is insufficient. return new CacheStrategy(null, null); } return candidate; } 复制代码
get():get()方法获取缓存策略,其中的核心方法为getCandidate()。
/** Returns a strategy to use assuming the request can use the network. */ private CacheStrategy getCandidate() { // No cached response. //无缓存 if (cacheResponse == null) { return new CacheStrategy(request, null); } // Drop the cached response if it is missing a required handshake. //丢弃请求是https且没有进行握手的缓存 if (request.isHttps() && cacheResponse.handshake() == null) { return new CacheStrategy(request, null); } // If this response should not have been stored, it should never be used // as a response source. This check should be redundant as long as the // persistence store is well-behaved and the rules are constant. //检测这个响应是否可以被缓存 if (!isCacheable(cacheResponse, request)) { return new CacheStrategy(request, null); } CacheControl requestCaching = request.cacheControl(); if (requestCaching.noCache() || hasConditions(request)) { return new CacheStrategy(request, null); } long ageMillis = cacheResponseAge(); long freshMillis = computeFreshnessLifetime(); if (requestCaching.maxAgeSeconds() != -1) { freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds())); } long minFreshMillis = 0; if (requestCaching.minFreshSeconds() != -1) { minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds()); } long maxStaleMillis = 0; CacheControl responseCaching = cacheResponse.cacheControl(); if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) { maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds()); } 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()); } // Find a condition to add to the request. If the condition is satisfied, the response body // will not be transmitted. String conditionName; String conditionValue; if (etag != null) { conditionName = "If-None-Match"; conditionValue = etag; } else if (lastModified != null) { conditionName = "If-Modified-Since"; conditionValue = lastModifiedString; } else if (servedDate != null) { conditionName = "If-Modified-Since"; conditionValue = servedDateString; } else { return new CacheStrategy(request, null); // No condition! Make a regular request. } Headers.Builder conditionalRequestHeaders = request.headers().newBuilder(); Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue); Request conditionalRequest = request.newBuilder() .headers(conditionalRequestHeaders.build()) .build(); return new CacheStrategy(conditionalRequest, cacheResponse); } 复制代码