Okhttp 缓存策略的获取
- 在缓存拦截器那我们提到过 第二步就是要获取一个缓存策略对象 CacheStrategy
CacheStrategy 是什么,有什么用
-
直接上代码
-
/** * 给定一个请求和缓存响应,这将确定是使用网络、缓存还是两者都使用。 */ public final class CacheStrategy { /** 在网络发送请求如果该Call不使用网络请求 该值为null 代表使用网络请求*/ public final @Nullable Request networkRequest; /**缓存的response 如果该Call 不适用缓存该值为null* 代表使用缓存/ public final @Nullable Response cacheResponse; }
-
总结
+ 是什么
+ 是一个内部维护一个Request 和一个Response 的类
+ 有什么用
+ 通过给内部的request 和response 的值,来确定该次请求是使用网络还是缓存,还是二者 都使用
+ 这里二者都用 就是对比网络和缓存的值 相同则返回缓存response 不同返回网络response
-
networkRequest,response 值是怎么生成的
-
要想了解怎么生成首先我们得知道在哪里通过什么方法获取的CacheStrategy,明显的这里是在CacheInterceptor中获取
-
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
-
可以看到是通过CacheStrategy.Factory的get方法 下面看下Factory的代码
-
public static class Factory { final long nowMillis; final Request request; final Response cacheResponse; /** 表示是服务器是在什么时候响应回客户端的 (如果已知的话) */ private Date servedDate; private String servedDateString; /** 缓存响应的最后修改日期(如果已知的话)。 * lastModified(包含在response里)与If-Modified-Since(包含在request里)配合使用 * 首次发送请求成功服务器会返回lastModified与If * 当再次发送该请求客户端会同时发送一个If-Modified-Since,而它的值就是lastModified的值 * 服务器接收到request后会取出值进行对比 若相同则服务器资源未改变可以使用缓存 * */ private Date lastModified; private String lastModifiedString; /** * 缓存响应的过期日期(如果已知的话)。 * 如果同时设置了该字段和max age,则首选max age。 */ private Date expires; /** * 由OkHttp设置的扩展头,指定缓存的HTTP请求首次发起时的时间戳。 */ private long sentRequestMillis; /** * 由OkHttp设置的扩展头,指定第一次接收缓存的HTTP响应时的时间戳。 */ private long receivedResponseMillis; /** * 服务器计算的出的一串值 用来对比同个请求服务器是否发生数据变化 * 缓存响应的标签。 一般和 if-non-match 配合 * 当首次发送请求成功服务器会返回一个etag * 当再次发送该请求 户端会同时发送一个If-None-Match,而它的值就是Etag的值 * 服务器接受到后对值进行比较 * 如果相同则 response 的code 就为HTTP_NOT_MODIFIED(304)数据没发生变化直接从缓存里取 */ private String etag; /** Age表示中间环节(CDN或缓存服务器)从服务器拿到数据之后的时间。 * 如果是客户端看到是0秒,代表是从服务器拿到的最新的数据。。 */ private int ageSeconds = -1; /**工厂类来获取策略 网络 缓存 或者二者都有* * @param nowMillis 当前时间 * @param request 当前请求 * @param cacheResponse 缓存响应 */ 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(); // 遍历header 更新 服务响应时间, 缓存响应过期时间,最后响应时间,Etag标签,从服务器拿到数据之后的时间 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); } } } } /** * 返回所满足的策略 网络 缓存 或者二者都有 */ public CacheStrategy get() { CacheStrategy candidate = getCandidate(); // networkRequest对象不为空且禁止使用网络 if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) { // 返回禁止使用网络且不能使用缓存的缓存策略对象 return new CacheStrategy(null, null); } return candidate; } /** * 如果请求包含避免服务器发送客户端本地响应的条件,则返回true。 * 当一个请求与它自己的条件一起排队时,内置的响应缓存将不会被使用。 * If-Modified-Since 最后响应时间 值为Modified * If-None-Match 值为 Etag 标签的值 * + 这两个值指示源自请求头If-Modified-Since或If-None-Match指定的版本后未被修改。 * + 这意味着不需要重新传输数据,客户端仍有以前缓存的数据 * */ private static boolean hasConditions(Request request) { return request.header("If-Modified-Since") != null || request.header("If-None-Match") != null; } }
-
可以看到具体生成规则就在getCandidate()方法中
-
/** 返回要使用的策略,假设请求可以使用网络。 */ private CacheStrategy getCandidate() { // 没有缓存的响应。则直接返回仅包含网络请求的策略 if (cacheResponse == null) { return new CacheStrategy(request, null); } // 如果是https 则判断响应是否握手 // 如果缓存请求缺少握手信息,则直接返回仅包含网络请求的策略 if (request.isHttps() && cacheResponse.handshake() == null) { return new CacheStrategy(request, null); } // 判断缓存response是否可用于另一个请求(如状态码是否正确等)如果不能用直接返回仅包含网络请求的策略 if (!isCacheable(cacheResponse, request)) { return new CacheStrategy(request, null); } CacheControl requestCaching = request.cacheControl(); // 判断缓存控制器是否nocache // 或者请求头包含If-Modified-Since( 最后响应时间),If-None-Match(Etag的值) // 这两值存在则需要服务器验证本地缓存是不是还能继续使用,所以返回仅包含网络请求的策略; if (requestCaching.noCache() || hasConditions(request)) { return new CacheStrategy(request, null); } CacheControl responseCaching = cacheResponse.cacheControl(); //省略部分代码 /** * 如果缓存控制器不是nocache,且ageMillis + minFreshMillis < freshMillis + maxStaleMillis * 缓存未过期可用,只是会在响应头添加110或者113的warning) * 则返回一个仅包含缓存的策略 */ 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()); } // 缓存过期,不可用 // 如果不存在条件If-None-Match或者If-Modified-Since则 返回一个包仅含网络请求的策略 // 如果存在条件If-None-Match或者If-Modified-Since将其添加到请求头 // 返回一个包含网络请求,和使用缓存的策略 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. } // 构建一个网络请求并设置header 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); }
-
-
-
生成规则总结
- 注 这里的
- 返回仅包含网络请求的策略表示 仅使用网络请求
- 返回仅包含缓存的策略 表示 仅使用缓存数据
- 返回既包含网络请求又包含缓存的策略 表示 二者都使用
- 由服务器判断缓存是否过期
- 没过期则使用缓存并更新缓存
- 过期了则使用网络并写入缓存 这部分在网络拦截器里
- 规则总结
- 使用网络请求策略
- 1、若当前没有缓存的响应。则直接返回仅包含网络请求的策略
- 2、 如果是https请求且缺少握手信息则 直接返回仅包含网络请求的策略
- 3、判断缓存response是否可用于另一个请求(如状态码是否正确等)如果不能用直接返回仅包含网络请求的策略
- 4 、判断缓存控制器是nocache,或者请求头包含If-Modified-Since,If-None-Match 返回仅包含网络请求的策略
- 5、缓存过期,不可用,不存在条件If-None-Match或者If-Modified-Since则 返回一个包仅含网络请求的策略
- 使用缓存策略
- 1、判断缓存控制器不是nocache,缓存未过期可用,则返回只包含缓存的策略
- 二者都使用
- 1、 缓存过期 ,且存在If-None-Match或者If-Modified-Since 则构建一个request并将其添加到reques的header ,此时返回既包含网络请求又包含缓存的策略
- 此时在缓存拦截器里就要进行304判断 ,来判断数据是否进行过更新了
- 1、 缓存过期 ,且存在If-None-Match或者If-Modified-Since 则构建一个request并将其添加到reques的header ,此时返回既包含网络请求又包含缓存的策略
作用
- 确定当前请求是使用网络、缓存还是两者都使用。