该系列OkHttp源码分析基于OkHttp3.14.0版本
概述
缓存响应以及返回之前的缓存的响应。整个拦截器可以分为两个部分,第一个部分为读取缓存,第二个部分为保存缓存。
需要注意的是,如果没有配置缓存管理的话,OkHttp是不会帮你缓存的,每次请求都会向服务器发起。但是OkHttp是自带了一个缓存管理的,一个名为Cache
的类。不过在OkHttpClient的缓存管理那里并没有默认配置。该类只需要两个参数,一个为缓存的文件目录(CacheDir
),一个为最大的缓存大小(maxSize
)。
源码分析
返回缓存
@Override public Response intercept(Chain chain) throws IOException {
//获取用户自己配置的缓存设置
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.
// 没有缓存的响应,又不发送网络请求,返回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();
}
// If we don't need the network, we're done.
// 不需要网络请求,直接返回缓存的响应
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
...省略部分代码
// If we have a cache response too, then we're doing a conditional get.
// 之前有缓存的响应结果
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {// 304
//服务器告诉客户端,之前的请求结果可以继续使用,所以这里使用缓存结果
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()).
// 在合并标头之后但在剥离Content-Encoding标头之前(由initContentStream()执行),更新缓存。
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);//更新缓存
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
...省略部分代码
}
根据源码可以看到,如果cache
为null的情况下,cacheResponse
也会为null,也就导致永远不会出现缓存了。这也证明我前面说的,OkHttp虽然自带了一个缓存管理类,但是却需要我们自己去配置到OkHttpClient
。
看完源码之后我们可以发现,是否返回缓存主要由3个参数决定的,一个是networkRequest
,一个是cacheResponse
,一个是请求后的状态码。
状态码的比较好理解,304表示服务器告诉客户端,之前缓存的资源是可以使用的。
另外两个变量的话,设计到OkHttp的缓存策略了,这个后面再讲。主要涉及到这几个类Cache
,CacheStrategy
。
缓存响应
返回缓存看完了,现在我们来看看OkHttp在缓存拦截器(CacheInterceptor
)中是如何去缓存服务端的响应的。
//下面开始请求网络
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
// 如果我们在I / O或其他方面崩溃,请不要泄漏高速缓存主体。
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// If we have a cache response too, then we're doing a conditional get.
// 之前有缓存的响应结果
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {// 304
...省略部分代码
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
// 在合并标头之后但在剥离Content-Encoding标头之前(由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.
}
}
}
可以看到,在拦截器中并没有设计到如何缓存的细节,只是判断了当前的响应是否符合缓存条件,具体的细节放在了cache
这个变量中了。目前的话暂时不分析具体的细节了,等以后再考虑详细分析OkHttp的缓存细节。