CacheInterceptor
介绍完缓存之后,现在开始介绍缓存拦截器CacheInterceptor了,同样也是查看其intercept()方法,这里边上片段代码边解析,化整为零:
@Override public Response intercept(Chain chain) throws IOException { Response cacheCandidate = cache != null ? cache.get(chain.request()) : null; ...}复制代码
首先通过判断缓存对象是否为null,如果不为null则根据传入的Chain对象的request获取缓存的Response。
@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()方法中:
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()方法更新统计指标, 更新网络请求数和命中缓存数;
接着通过判断CacheStrategy的networkRequest和cacheResponse,如果二者同时为null,即当前没有网络且没有缓存,则构造一个Response对象并返回,其中状态码为504,并设置了提示的message信息。
接着分析:
@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,接着将最新的数据缓存并移除无效的缓存。
总结CacheInterceptor主要做的操作:
- 从缓存中获取Reponse对象,赋值给caceResponse,如果找不到缓存则为null;
- 根据当前请求request和caceResponse 构建一个CacheStrategy对象;
- CacheStrategy这个策略对象将根据相关规则来决定cacheResponse和Request是否有效,如果无效则分别将cacheResponse和request设置为null;
- 如果request和cacheResponse都为null,即没有网络且没有缓存,直接返回一个状态码为504的空Respone对象;
- 如果resquest为null而cacheResponse不为null,即没有网络且有缓存,则直接返回cacheResponse对象;
- 执行下一个拦截器的intercept()方法进行网络请求,获取response;
- 如果服务器资源没有过期(状态码304)且存在缓存,则返回缓存;
- 如果服务器资源有修改,则将返回的最新数据进行缓存并移除掉无效的缓存,最后返回response对象给上一个拦截器。
下一篇将讲解五大拦截器中的最后两个拦截器, ConnectInterceptor和CallServerInterceptor,感兴趣的朋友可以继续阅读: