缓存拦截器就是处理缓存数据,这个思路上不复杂,如果你们配置使用缓存数据,就会根据缓存策略判断是否能使用缓存的数据,如果可以就取缓存的数据,否则,则去请求网络。
1.CacheInterceptor
@Override public Response intercept(Chain chain) throws IOException {
/** 首先判断缓存对象是否为null,非空就获取缓存对象,否则null,且给Response 赋值
*cache对象是谁, InternalCache cache; 由当前缓存拦截器构造的时候传入。*InternalCache 是个接口,所以去看实现类,但是实现类没有,但是在okhttp3.Cache里找
*到了InternalCache 的内部类实例,同时还重写相应的方法。get put remove update等方法。
下面细讲
*/
Response cacheCandidate = cache != null ? cache.get(chain.request()) : null;
long now = System.currentTimeMillis();
//CacheStrategy 缓存策略对象,工厂模式,最后get()获取
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
cache.trackResponse(strategy);
}
//缓存对象不为空,但是缓存的响应对象null,关闭缓存
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.
// 网络请求对象null 缓存对象null 直接返回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.
// 请求对象为null,返回缓存的对象,该对象是null的body对象,
//stripBody方法去除了body
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're doing a conditional get.
// 如果缓存对象不为空,且返回304-重定向无修改数据,合并缓存的header和网络的header
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();
// 判断是否有body
if (HttpHeaders.hasBody(response)) {
// 判断是否请求方法合法,因为缓存只处理GET请求,不合法从缓存对象移除
CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
// 写入响应对象到缓存
response = cacheWritingResponse(cacheRequest, response);
}
return response;
}
2.缓存底层实现
okHttp使用InternalCache cache实现缓存数据的处理。
/**
* OkHttp's internal cache interface. Applications shouldn't implement this: instead use {@link
* okhttp3.Cache}.
*/
public interface InternalCache
是个接口,请查阅okhttp3.Cache
final InternalCache internalCache = new InternalCache() {
@Override public Response get(Request request) throws IOException {
return Cache.this.get(request);
}
@Override public CacheRequest put(Response response) throws IOException {
return Cache.this.put(response);
}
@Override public void remove(Request request) throws IOException {
Cache.this.remove(request);
}
@Override public void update(Response cached, Response network) {
Cache.this.update(cached, network);
}
@Override public void trackConditionalCacheHit() {
Cache.this.trackConditionalCacheHit();
}
@Override public void trackResponse(CacheStrategy cacheStrategy) {
Cache.this.trackResponse(cacheStrategy);
}
};
2.1 put 保存数据
@Nullable CacheRequest put(Response response) {
//请求方法
String requestMethod = response.request().method();
//验证缓存的有效期
if (HttpMethod.invalidatesCache(response.request().method())) {
try {
remove(response.request());
} catch (IOException ignored) {
// The cache cannot be written.
}
return null;
}
// 非get方法不缓存
if (!requestMethod.equals("GET")) {
// Don't cache non-GET responses. We're technically allowed to cache
// HEAD requests and some POST requests, but the complexity of doing
// so is high and the benefit is low.
return null;
}
if (HttpHeaders.hasVaryAll(response)) {
return null;
}
//Entry是包装了所有响应的信息体,
Entry entry = new Entry(response);
DiskLruCache.Editor editor = null;
try {
//通过url作为key
editor = cache.edit(key(response.request().url()));
if (editor == null) {
return null;
}
//写入到磁盘缓存里
entry.writeTo(editor);
return new CacheRequestImpl(editor);
} catch (IOException e) {
abortQuietly(editor);
return null;
}
}
2.2 Entry
private static final class Entry {
/** Synthetic response header: the local time when the request was sent. */
private static final String SENT_MILLIS = Platform.get().getPrefix() + "-Sent-Millis";
/** Synthetic response header: the local time when the response was received. */
private static final String RECEIVED_MILLIS = Platform.get().getPrefix() + "-Received-Millis";
private final String url;
private final Headers varyHeaders;
private final String requestMethod;
private final Protocol protocol;//协议
private final int code;
private final String message;
private final Headers responseHeaders; //响应头部
private final @Nullable Handshake handshake;// 握手
private final long sentRequestMillis;
private final long receivedResponseMillis;