OkHttp 缓存管理

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实现缓存
    总结
    • 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值