OkHttp 缓存管理
- okhttp内置缓存策略,在CacheInterceptor中执行缓存策略,
- 那么
- 我们如何设置使用Okhttp的缓存
- okhttp 缓存又是如何管理的呢
如何设置使用Okhttp的缓存
-
在构建OkHttpClient的时候即可设置okhttp的缓存
-
OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) //缓存目录 ,缓存空间大小 .cache(new Cache(new File(this.getExternalCacheDir(), "okhttpcache"), 10 * 1024 * 1024)) .build()
-
由以上代码可知设置缓存通过OkhttpClient 设置一个缓存对象即可
-
若有其他缓存要求可通过给request 设置
CacheControl
配合使用 或者使用CacheControl
常量-
使用
CacheControl
-
CacheControl cc = new CacheControl.Builder() //不使用缓存,但是会保存缓存数据 .noCache() //不使用缓存,同时也不保存缓存数据 .noStore() //只使用缓存,(如果我们要加载的数据本身就是本地数据时,可以使用这个,不过目前尚未发现使用场景) .onlyIfCached() //手机可以接收响应时间小于当前时间加上10s的响应 .minFresh(10,TimeUnit.SECONDS) //手机可以接收有效期不大于10s的响应 .maxAge(10,TimeUnit.SECONDS) //手机可以接收超出5s的响应 .maxStale(5,TimeUnit.SECONDS) .build(); Request request = new Request.Builder() .cacheControl(cc) .url("http://www.xxx.com.cache").build();
-
-
使用
CacheControl
常量-
Request request = new Request.Builder() //强制使用网络 .cacheControl(CacheControl.FORCE_NETWORK) //强制使用缓存 .cacheControl(CacheControl.FORCE_CACHE) .url("http://www.xxx.com/cache").build();
-
-
-
缓存管理
-
通过上面使用的介绍我们知道
- 通过设置Cache类对象即可使用缓存
- 由此我们猜想管理相关操作都在Cache类里
-
下面我们看下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; // 1、OKHttp的内部缓存接口,内部提供了 get put remove update 等操作 // 同时 okhttp的缓存拦截器正是使用 internalCache 来 put get remove update 数据的 final InternalCache internalCache = new InternalCache() { @Override public @Nullable Response get(Request request) throws IOException { return Cache.this.get(request); } @Override public @Nullable 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 .Cache 内部是用的DiskLruchche 实现的缓存 final DiskLruCache cache; }
-
我们通过观察
- cache 类内部,通过匿名内部类实现了一个接口 InternalCache 并实现了接口中的方法
- 分别是put、get、remove 、update 、trackConditionalCacheHit、trackResponse
- 根据这些方法名称基本可以断定 是由该接口来负责
- 结合缓存拦截器那里提到的,该接口的作用就是为为缓存类Cache 提供增删改查的方法供缓存拦截器使用
- cache 类内部,有一个DiskLruCache,此处我们猜测,OKhttp 通过
DiskLruCache
实现缓存
- cache 类内部,通过匿名内部类实现了一个接口 InternalCache 并实现了接口中的方法
总结
- OKhttp 通过
DiskLruCache
实现缓存 Cache
类封装了缓存的实现,InternalCache
来完成缓存管理相关缓存操作 写入 取出 更新 删除等操作
-
数据是如何通过DiskLrucache进行存储的呢
-
我们以put get为例
-
put
-
@Nullable CacheRequest put(Response response) { String requestMethod = response.request().method(); //```` // 1 先构建一个Entry对象 Entry entry = new Entry(response); DiskLruCache.Editor editor = null; try { // 2 创建editor对象 以请求的url 计算得出的key 值为key editor = cache.edit(key(response.request().url())); // 3 通过 writeTo 缓存数据 这里缓存的是request // 那response 在哪里缓存呢 继续看 entry.writeTo(editor); // 4 创建CacheRequest 的实现类 CacheRequestImpl(editor) 传入Editor并返回 // 也就是在这里缓存的response // 创建该接口是为了将其暴露给缓存拦截器,方便拦截器使用CacheRequestImpl实现类来更新和写入缓存数据。同时也是在这里将body也是就response 写入缓存的 return new CacheRequestImpl(editor); } catch (IOException e) { // 终止editor abortQuietly(editor); return null; } }
-
-
总结
-
1 先构建一个Entry对象 Entry 要缓存的信息的包装类
-
2 创建editor对象
- 以request 的url 生成的key作为该条request缓存的key
- 后面取出 也是通过该key来获取对应的response
-
3 通过 writeTo 缓存请求信息
-
4 创建CacheRequest 的实现类 CacheRequestImpl(editor) 传入Editor并返回,在这里缓存了Response
-
创建该接口实现类是为了将其暴露给缓存拦截器 方便其更新写入缓存数据
-
看下CacheRequestImpl中的代码
-
CacheRequestImpl(final DiskLruCache.Editor editor) { this.editor = editor; this.cacheOut = editor.newSink(ENTRY_BODY); // body 就是返回的response this.body = new ForwardingSink(cacheOut) { @Override public void close() throws IOException { synchronized (Cache.this) { if (done) { return; } done = true; writeSuccessCount++; } super.close(); // 提交写入磁盘 editor.commit(); } }; }
-
-
-
-
get
-
@Nullable Response get(Request request) { String key = key(request.url()); DiskLruCache.Snapshot snapshot; Entry entry; // 1 获取 snapshot(缓存快照 记录缓存在某个时刻所包含的内容) snapshot = cache.get(key); // 2 创建entry entry = new Entry(snapshot.getSource(ENTRY_METADATA)); // 3 通过entry的respnse方法,获取response Response response = entry.response(snapshot); // 4 判断key 是否对应 对应返回 否则关闭 if (!entry.matches(request, response)) { Util.closeQuietly(response.body()); return null; } // 返回response return response; }
-
-
get 总结
-
1 获取 snapshot(缓存快照 记录缓存在某个时刻所包含的内容)
-
2 通过snapshot 创建 entry
-
3 通过entry的respnse方法,获取response
-