开头
首先我们回忆下在Okhttp中如何使用缓存:
//缓存目录
File file = new File(getCacheDir(), "http");
//创建缓存,这里指定了缓存为100M,如果查出就情况该目录
Cache cache = new Cache(file, 1024 * 1024 * 100);
//然后使用cache方法添加一个cache
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.cache(cache)
.addNetworkInterceptor(FORCE_CACHE_NETWORK_DATA_INTERCEPTOR)
.build();
Request request = new Builder()
.url("https://api.github.com/users/lifengsofts")
// .url("http://me.woblog.cn/")
.build();
try {
Response response = okHttpClient.newCall(request).execute();
if (response.isSuccessful()) {
Log.d(TAG, "response: " + bodyString(response));
//缓存的response
//使用了缓存,他返回null
Log.d(TAG, "networkResponse: " + bodyString(response.networkResponse()));
//如果没有缓存,或者不适用缓存,缓存过期返回null
Log.d(TAG, "cacheResponse: " + bodyString(response.cacheResponse()));
} else {
Log.d(TAG, "error1: " + response.code());
}
} catch (IOException e) {
e.printStackTrace();
}
可以发现首先需要配置缓存目录,缓存大小,然后就按照正常的方法请求网络,这样就开启了默认缓存策略。
同时我们还可以配置单个请求的缓存:
//强制使用缓存
request = new Builder()
.url("https://api.github.com/users/lifengsofts")
.cacheControl(CacheControl.FORCE_CACHE)
.build();
//强制使用网络
request = new Builder()
.url("https://api.github.com/users/lifengsofts")
.cacheControl(CacheControl.FORCE_NETWORK)
.build();
后面我们就基于上面的使用入门来分析Okhttp的缓存实现。
RealCall#getResponseWithInterceptorChain
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
//添加了OkhttpClient在初始化添加的拦截器
//前面我们使用的日志拦截器就添加到这里的
interceptors.addAll(client.interceptors());
//重试,重定向
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//处理缓存的拦截器
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
//在这个拦截器才发起请求网络的请求
interceptors.add(new CallServerInterceptor(forWebSocket));
//创建一个拦截器链,并传入了所有的拦截器。
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest, this, eventListener);
//最后调用了拦截器链的proceed来处理
//在这里我们可以才到拦截器的调用应该就在改方法里面
return chain.proceed(originalRequest);
}
在getResponseWithInterceptorChain方法中我们看到添加CacheInterceptor时传入了client.internalCache(),我们先来看看client.internalCache()是什么:
2.点击跳转
-->点击跳转: internalCache
InternalCache internalCache() {
return cache != null ? cache.internalCache : internalCache;
}
//这个cache是什么: Cache对象,是一个成员变量
从哪里传递进来到的呢?
通过源码可以看到是Builder中的Cache方法传递进来的
public Builder cache(@Nullable Cache cache) {
this.cache = cache;
//这里把原来的OkhttpClien.Builder中的internalCache置为null
//从上面的internalCache方法可以看出,既然调用了cache,那么就用不
//internalCachel了,用的是cache.internalCache
this.internalCache = null;
return this;
}
/** Sets the response cache to be used to read and write cached responses. */
//这个setInternalCache可以不用关心在哪里调用的,
void setInternalCache(@Nullable InternalCache internalCache) {
this.internalCache = internalCache;
this.cache = null;
}
====================================================
--》回到方法调用原处,并点击跳转到CacheInterceptor
public final class CacheInterceptor implements Interceptor {
//InternalCache 是个接口在哪里实现了这个接口呢,可以查看Cache类里面实现了这个接口
final InternalCache cache;
public CacheInterceptor(InternalCache cache) {
this.cache = cache;
}
@Override
public Response intercept(Chain chain) throws IOException {
//首先根据cache看看有没有缓存的response
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
//然后根据当前时间戳,request,上次缓存的response创建一个cache策略
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
//根据这个策略用网络的请求还是用缓存(逻辑放到这CacheStrategy策略里面去了)
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;//上次缓存的response
if (cache != null) {
//这里其实是一个监听器,可以跟踪缓存的执行流程
cache.trackResponse(strategy);
}
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
// 如果禁止使用网络,同时获取的缓存也为空,就返回一个504错误
//在学习时,说到这种情况,会得到504响应,那个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)
//发送request的时间为 -1L
.sentRequestAtMillis(-1L)
//接收response的时间是当前的时间
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// 如果配置的缓存不需要网络,那就可以直接返回了,返回的body是空
// 这个可以理解为:如果networkrequest为空,表示不需要请求网络,所以说就返回缓存的响应
if (networkRequest == null) {
return cacheResponse.newBuilder()
//-->点击stripBody详情看下面
//stripBody:缓存可能没空,所以说就用这个方法处理一下
.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) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {//304
//这个值没有修改的话
//这里主要就是将缓存的头和网络响应头组合
Response response = cacheResponse.newBuilder()
//将缓存的headers和网络的headers进行一个合并
-->跳转到combine
.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实现在分析
//关键点是这个 将cacheResponse(上次缓存的response)和response(本次缓存的)更新了
//分析的逻辑,后面再分析
--》跳转cache.update
//cache: InternalCache cache是一个接口,实现类在Cache中,
--》跳转到Cache类
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
//从if可以看出,走到了这一步,说明缓存内容为空,那么这个时候就需要去请求网络了
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// 如果当前这个http有返回的body,并且可以缓存,那就缓存他
CacheRequest cacheRequest = cache.put(response);
//cacheWritingResponse:将缓存的response写入到文件里面
//-->点击跳转:cacheWritingResponse
return cacheWritingResponse(cacheRequest, response);
}
//如果http不能缓存,也就是像post,PATCH等这类请求不能缓存,就移除缓存
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
//走了这一步,这个response不管是从本地还是网络,都是有值的
return response;
}
--》点击跳转:Cache类
public final class Cache implements Closeable, Flushable {
private static final int VERSION = 201105;
private static final int ENTRY_METADATA = 0;
private static final int ENTRY_BODY = 1;
private static final int ENTRY_COUNT = 2;
//可以发现他将真正的实现代理到外面的类,这个也和桥接类似,将实现交给他人
//这样CacheInterceptor就不用直接依赖他的实现类
final InternalCache internalCache = new InternalCache() {
@Override
public Response get(Request request) throws IOException {
//这里调用Cache外层类的方法
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);
}
};
--》点击跳转:stripBody
//缓存可能没空,所以说就用这个方法处理一下
private static Response stripBody(Response response) {
return response != null && response.body() != null
? response.newBuilder().body(null).build()
: response;
}
跳转到combine
/**
* Combines cached headers with a network headers as defined by RFC 2616, 13.5.3.
*
* networkHeaders: 就上面通过这个方法chain.proceed(networkRequest)获取到的的response
*/
private static Headers combine(Headers cachedHeaders, Headers networkHeaders) {
//最终的header存到这里
Headers.Builder result = new Headers.Builder();
//先遍历cachedHeaders,也就说说,后面的networkHeaders会覆盖相同的字段
for (int i = 0, size = cachedHeaders.size(); i < size; i++) {
//拿到名称和值
String fieldName = cachedHeaders.name(i);
String value = cachedHeaders.value(i);
//删除一些请求头
if ("Warning".equalsIgnoreCase(fieldName) && value.startsWith("1")) {
continue; // Drop 100-level freshness warnings.
}
//如果不是端到端,或者networkHeaders中不存在的头添加到结果header中
//所谓端到端简单理解为,如果有一个代理,有些请求头只从客户端发给代理,代理不用发给服务端的头
//fieldName:就是请求头里面的key,
//-->跳转:isEndToEnd(fieldName)
if (!isEndToEnd(fieldName) || networkHeaders.get(fieldName) == null) {
//Internal是个抽象类,它的子类在OkhttpClient static静态代码中
//其实最终是添加到Headers.Builder中的集合中的
//查看代码知道: builder.addLenient(name, value);--》
/*
Builder addLenient(String name, String value) {
namesAndValues.add(name);
namesAndValues.add(value.trim());
return this;
而namesAndValues是 Headers.Builder里面的List<String>集合
其实是:把fieldName和value添加到result(Headers.Builder:上面创建的局部变量)中的
}
*/
Internal.instance.addLenient(result, fieldName, value);
}
}
//遍历网络请求头
//网络请求也是上面的逻辑
//networkHeaders: 就上面通过这个方法chain.proceed(networkRequest)获取到的的response
for (int i = 0, size = networkHeaders.size(); i < size; i++) {
String fieldName = networkHeaders.name(i);
if ("Content-Length".equalsIgnoreCase(fieldName)) {
//这种Content-Length直接跳过了,不用添加
continue; // Ignore content-length headers of validating responses.
}
if (isEndToEnd(fieldName)) {
//result: Headers.Builder
Internal.instance.addLenient(result, fieldName, networkHeaders.value(i));
}
}
return result.build();
}
-->跳转:isEndToEnd(fieldName)
/**
* Returns true if {@code fieldName} is an end-to-end HTTP header, as defined by RFC 2616,
* 13.5.1.
*
* fieldName:就是请求头里面的key
*/
static boolean isEndToEnd(String fieldName) {
//如果请求头里面的key是下面的这些值,那么就是端到端
return !"Connection".equalsIgnoreCase(fieldName)
&& !"Keep-Alive".equalsIgnoreCase(fieldName)
&& !"Proxy-Authenticate".equalsIgnoreCase(fieldName)
&& !"Proxy-Authorization".equalsIgnoreCase(fieldName)
&& !"TE".equalsIgnoreCase(fieldName)
&& !"Trailers".equalsIgnoreCase(fieldName)
&& !"Transfer-Encoding".equalsIgnoreCase(fieldName)
&& !"Upgrade".equalsIgnoreCase(fieldName);
}
跳转到Cache# InternalCache# update
--》跳转到Cache类
可以看到internalCache 接口的实现类InternalCache
public final class Cache implements Closeable, Flushable {
private static final int VERSION = 201105;
private static final int ENTRY_METADATA = 0;
private static final int ENTRY_BODY = 1;
private static final int ENTRY_COUNT = 2;
//可以发现他将真正的实现代理到外面的类,这个也和桥接类似,将实现交给他人
//这样CacheInterceptor就不用直接依赖他的实现类
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的update
Cache.this.update(cached, network);
}
@Override
public void trackConditionalCacheHit() {
Cache.this.trackConditionalCacheHit();
}
@Override
public void trackResponse(CacheStrategy cacheStrategy) {
Cache.this.trackResponse(cacheStrategy);
}
};
-》跳转到Cache外部类update
void update(Response cached, Response network) {
Entry entry = new Entry(network);
DiskLruCache.Snapshot snapshot = ((CacheResponseBody) cached.body()).snapshot;
DiskLruCache.Editor editor = null;
try {
editor = snapshot.edit(); // Returns null if snapshot is not current.
if (editor != null) {
//将entry写入到缓存
entry.writeTo(editor);
editor.commit();
}
} catch (IOException e) {
abortQuietly(editor);
}
}
跳转:CacheInterceptor#cacheWritingResponse
/**
* Returns a new source that writes bytes to {@code cacheRequest} as they are read by the source
* consumer. This is careful to discard bytes left over when the stream is closed; otherwise we
* may never exhaust the source stream and therefore not complete the cached response.
*/
private Response cacheWritingResponse(final CacheRequest cacheRequest, Response response)
throws IOException {
// 做一些参数的校验
if (cacheRequest == null) return response;
Sink cacheBodyUnbuffered = cacheRequest.body();
if (cacheBodyUnbuffered == null) return response;
//获取到响应的body(是个输出流)
final BufferedSource source = response.body().source();
//相当于缓存的输入流
final BufferedSink cacheBody = Okio.buffer(cacheBodyUnbuffered);
//后面的逻辑就是将响应的流写入到缓存的body
Source cacheWritingSource = new Source() {
boolean cacheRequestClosed;
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead;
try {
//将响应全部流读取到sink中
bytesRead = source.read(sink, byteCount);
} catch (IOException e) {
if (!cacheRequestClosed) {
cacheRequestClosed = true;
cacheRequest.abort(); // Failed to write a complete cache response.
}
throw e;
}
//如果读取失败,直接返回
if (bytesRead == -1) {
if (!cacheRequestClosed) {
cacheRequestClosed = true;
cacheBody.close(); // The cache response is complete!
}
return -1;
}
//到这里,就表示读取到流,现在需要将这些流读取到缓存的body对应的buffer中
sink.copyTo(cacheBody.buffer(), sink.size() - bytesRead, bytesRead);
//这里还发布一个完成的消息,但该知识到了okio这个包中,后面在分析
cacheBody.emitCompleteSegments();
return bytesRead;
}
@Override
public Timeout timeout() {
return source.timeout();
}
@Override
public void close() throws IOException {
if (!cacheRequestClosed
&& !discard(this, HttpCodec.DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)) {
//丢弃上面read方法中读取到当前source的流
cacheRequestClosed = true;
cacheRequest.abort();
}
source.close();
}
};
//最后重新构建一个响应返回
return response.newBuilder()
.body(new RealResponseBody(response.headers(), Okio.buffer(cacheWritingSource)))
.build();
}