Okhttp的缓存拦截器CacheInterceptor
- okhttp 提供了 CacheInterceptor 来处理缓存
- 关于如何使用缓存请参考缓存管理
- 下面我们分析下该拦截器的工作流程
流程分析
- 主要流程都在intercept 方法里 下面看代码
public final class CacheInterceptor implements Interceptor {
final @Nullable
// 注意观察 这里InternalCache
InternalCache cache;
@Override public Response intercept(Chain chain) throws IOException {
// 1、读取候选缓存 如果缓存不为空 则获取缓存中 request 对应的 response
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
// 2、获取缓存策略 内部维护了 一个request 和一个 response
// 该类作用 判断当前请求适用与网络或者缓存 或者两者都用(强制缓存、对比缓存)
// networkRequest 代表网络请求
// cacheResponse 代表使用缓存
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
// 3、如果缓存不为空 更新统计指标, 增加命中率
if (cache != null) {
cache.trackResponse(strategy);
}
// 4、如果候选缓存不适用 则关闭该缓存响应
// 候选缓存不为空 与 缓存策略为空(及不允许使用缓存) 来表示候选缓存无效
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
// 5、根据策略进行判断 如果禁止使用网络 但是缓存也没有 则构建response返回504
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();
}
// 6、根据策略 如果不允许使用网络,有缓存的 直接返回 缓存中的response
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
//7、如果允许使用网络 则 进行网络请求
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
// 如果出现io 异常 同时 预选缓存不为空 则直接关闭最新的
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// 8、如果缓存不为空 则判断最新获取的networkresponse的code
// 是不是等于 HTTP_NOT_MODIFIED 304 如果是则使用缓存 并更新缓存
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());
}
}
//9、 如果缓存response为空则读取网络请求response
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
// 10、 如果cache不为空 且满足缓存条件 - 存在body 与 策略可缓存 则缓存response
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);
}
// 11、 判断是否get请求 不是则直接 在缓存中移除当前请求
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
12、 最后返回response
return response;
}
}
流程总结
-
- 首先判断缓存是否为空,不为空,获取预选缓存的resposne
- 获取一个CacheStrategy 缓存策略对象(此处是一个重点)
- 该类主要是用于解决到底使用网络还是缓存,亦或是两者皆用
- 缓存策略的获取请参看缓存策略获取
- 判断预选缓存response是否有效若无效则关闭预选缓存response
- 判断条件
- 存在预选缓存且缓存策略中不允许使用缓存就表示预选缓存无效
- 判断条件
- 根据缓存策略判断如果不允许使用网络且缓存策略也没有,构建response设置code504 并返回
- 根据缓存策略判断如果不允许使用网络请求且存在缓存 则直接返回缓存的response
- 如果允许使用网络则获取网络响应,判断缓存response是否为空
- 如果不为空,则判断最新获取的response的code是否为304(304 表示数据没发生变化)
- 是 则返回缓存response 并更新缓存
- 否 则关闭缓存response
- 如果为空,顺序执行,读取网络请求的response
- 如果不为空,则判断最新获取的response的code是否为304(304 表示数据没发生变化)
- ,判断缓存Cache是否为空(此处开始后面则有缓存逻辑)
- 若为空则不处理
- 若不为空则判断该request 是否满足缓存条件
- 若满足,则缓存该次请求 并返回此次网络请求的响应response
- 若不满足,则不处理,
- 判断是否get请求 若不是则在缓存中直接移除此次请求
- 返回读取到的网络请求的response
缓存拦截器作用总结
-
根据策略完成数据的缓存和读取
-
根据获取到的缓存策略决定从哪读取respnse(网络或者缓存或者对比数据)并返回response给上一个拦截器
-
调用proceed方法执行下一个拦截器 ->ConnectionInterceptor
注意
- 在 CacheInterceptor类里我们看到存在一个 InternalCache 的对象cache 且为final 并不允许为空
- 并在后面缓存数据的时候是通过cache.put
- 猜测 这个InternalCache 就是用来 对数据进行系列缓存操作的
- 这是什么,用来做什么 ?
- InternalCache 是一个缓存内部接口
- 为缓存类Cache 提供增删改查的方法供外界使用 在缓存管理里会提到