在上一篇Okhttp解析(一)– 源码中,我们从源码角度分析了同步请求和异步请求的流程,这一篇我们主要是分析Okhttp的缓存实现。那么Okhttp的缓存是如何使用的呢,下面是简单的示例代码:
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.cache(new Cache(文件对象,1024*1024*5))
.build();
其实很简单就是在构建OkhttpClient的时候连缀了一个cache函数,并传入一个Cache实例,Cache构造要求两个参数,第一个是缓存的文件对象;第二个是缓存的大小(以字节为单位)。下面我们来看下Cache类。
我们从它的构造开始:
public Cache(File directory, long maxSize) {
this(directory, maxSize, FileSystem.SYSTEM);
}
Cache(File directory, long maxSize, FileSystem fileSystem) {
this.cache = DiskLruCache.create(fileSystem, directory, VERSION, ENTRY_COUNT, maxSize);
}
我们发现两个参数的构造会调用三个参数的构造,在该构造中会实例化一个DiskLruCache硬盘缓存对象,那么这个硬盘缓存对象是什么呢?
它是一个基于Lru算法的有限存储空间的文件系统,每个缓存实体都是以key-value的形式存在,key必须能够匹配正则[a-z0-9_-]{1,64}
,value是以字节序列的形式存在的,长度在0-Integer.MAX_VALUE之间。
这里我们介绍一下Lru算法吧,然后再继续。 Lru是Least Recently Used的缩写,即最近最少使用,假设我们的缓存一共可以存储1000条数据,当实际存储量小于1000时随便添加,但是当存储量超过1000呢?它会从之前缓存的数据中删除最老的一条,从而腾出空间保存新的。不过需要注意一下,所谓的最老并不是说就是第一条保存的数据,比如:第一次缓存第一条数据,在第1000次我们又请求了该数据,那么它就会保存在最前面(即最新),其它的所有保存的数据都会被顶到后面去。具体的实现,感兴趣的话可以自己去了解一下。
好了缓存现在已经配置了,那么它会在哪里使用呢?答案就是CacheInterceptor,我们去看一下它的intercept函数:
@Override public Response intercept(Chain chain) throws IOException {
//判断缓存类是否为null,按照上面的示范代码,默认是null,因为在初始化CacheInterceptor
//类时会传入一个InternalCache对象,而在使用空构造函数创建OkhttpClient时,
//默认的Builder是没有默认的InternalCache对象的
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
...
}
第一句就是我们想要的,其它的暂时省略。判断cache是否为null,不为null传入请求获取Response对象,认真的朋友可能会发现这个cache好像不是我们实例化的Cache对象,而是一个InternalCache对象,瞬间凌乱了。。。不要急,我们看下在初始化CacheInterceptor时传入的是什么?
//RealCall的getResponseWithInterceptorChain函数
Response getResponseWithInterceptorChain() throws IOException {
...
interceptors.add(new CacheInterceptor(client.internalCache()));
...
}
原来是调用OkhttpClient的internalCache函数:
InternalCache internalCache() {
return cache != null ? cache.internalCache : internalCache;
}