Volley详解(四)——缓存(Cache)

来源:互联网 发布:bad air sponge 知乎 编辑:程序博客网 时间:2024/06/06 15:28

概述

本篇介绍缓存相关的类,包括CacheDiskBasedCacheCacheDispatcher


Part 1 ——Cache

表示缓存的接口,这也体现了扩展性,只给出存取接口,而没有具体实现,虽然Volley中只给了一中实现,即DiskBasedCache,但客户端在调用时仍然只与接口打交道,如果想用其他的缓存方式,只需要自己实现Cache接口即可,完全不影响客户端的工作。

内部类Entry

静态内部类Cache$Entry,表示具体的缓存内容,主要属性:

  • byte[] data:响应的主体
  • long serverDate:服务器返回数据的时间
  • long lastModified:内容最后更新的时间
  • long ttl:数据保质期
  • Map<String, String> responseHeaders:响应的头信息

主要方法

Cache接口主要有如下方法:

  • Entry get(String key);
    根据key获取缓存内容
  • put(String key, Entry entry);
    向缓存中写入内容
  • remove(String key);
    删除缓存条目
  • clear();
    清空缓存

Part 2 ——DiskBasedCache

VolleyCache接口的唯一默认实现,用文件存储的方式实现,并根据最近最少使用(LRU)原则限制缓存容量。

内部类CacheHeader

定义了内部类DiskBasedCache$CacheHeader,从名字可以看出该类存储的主要是header,实际上,它的多数成员都与Cache$Entry类相同,比如头信息和过期策略相关的信息等,唯一不同的是,该类不包含byte[] data,也就是响应的内容主体,很好理解,因为既然是基于Disk的,那这部分内容显然是要放在文件里的。

其实,CacheHeader类起到了索引的作用,辅助信息除了写入文件,还要放在CacheHeader类中,也就是在内存里。

这样做的合理性一目了然:

CacheHeader里的信息会被更频繁地用到,比如判断缓存是否存在、是否过期,每次访问缓存,都要先查询这些辅助信息;而且这些辅助信息占用空间很小。因此,这部分放在内存。

DiskBasedCache中维护了一个Map<String,CacheHeader> mEntries,用来作缓存索引。

get()put()

这是读写缓存的方法,二者思路大同小异。在DiskBasedCache中,Entry实际上起到了中间层的作用,这是与外界的交换方式,而CacheHeader和File是DiskBasedCache内部的存储方式。

put()写入缓存时,先根据cacheKey找到对应的文件,然后依次向文件写入Headerbody,最后将Header加入到mEntries中作为索引。

get()读取缓存时,先从mEntries中查找所需内容是否存在、是否过期。

  • 如果缓存存在且未过期,则从mEntries中获取CacheHeader,然后根据cacheKey找到对应的文件,从文件获取body,将CacheHeaderbody组合成Cache$Entry并返回结果;
  • 如果缓存不存在或过期,则直接将该请求放入网络请求队列,等待网络分发线程处理。

缓存空间限制

DiskBasedCache中用作索引的对象是mEntries,它是由LinkedHashMap实现的,而且存储顺序是访问顺序accessOrder(与插入顺序insertOrder相对),可以实现最近最少使用(LRU)原则。

实现控制缓存大小的函数是

private void pruneIfNeeded(int neededSpace);

每次需要写入缓存之前,该函数会根据本次写入所需的空间判断是否需要进行清理缓存。如果需要清理,则获取mEntriesiterator,依次删除索引及对应的文件,知道缓存空间低于限制的最大空间。

对于以accessOrder作为访问顺序的LinkedHashMapiterator会按照最近访问的时间顺序进行遍历,最长时间没有被访问过的条目会被最先遍历到。因此该数据结构可以直接用来实现LRU Cache。


Part 3 ——CacheDispatcher

缓存分发线程,继承了Thread,维护了缓存请求队列mCacheQueue,网络请求队列mNetworkQueue,缓存mCache,响应分发对象mDelivery等,负责从缓存请求队列mCacheQueue中依次获取请求并进行处理,如果队列为空,则阻塞,直到队列不为空。

对于获取到的每个Request,从mCache查找缓存,如果获取到了未过期的缓存,则将得到的Cache$Entry构造成NetworkResponse对象,交给mDelivery进行毁掉处理。

如果在mCache中没有找到缓存,则将该请求放入mNetworkQueue,等待网络分发线程处理。

如果在mCache中找到了过期的缓存,则除了将请求放入mNetworkQueue之外,还要删除mCache中过期的缓存。

0 0
原创粉丝点击