Android 缓存策略
一般来说,缓存策略主要包含缓存的添加、获取和删除。如何添加和获取缓存这个比较好理解,那为什么还要删除缓存呢?这是因为不管是内存缓存还是硬盘缓存,它们的缓存大小都是有限的。当缓存满了之后,再向其添加缓存,就需要先删除旧的缓存。因此 LRU 缓存算法应运而生。
LRU(Least Recently Used),最近最少使用算法,核心思想是当缓存满时,优先淘汰那些近期最少使用的缓存对象,有效的避免了 OOM 的出现。Android 中采用 LRU 算法的常用缓存有两种:LruCache 和 DisLruCache,分别用于实现内存缓存和硬盘缓存。
LRU 缓存的实现类似于一个特殊的栈,把访问过的元素放置到栈顶(若栈中存在,则更新至栈顶;若栈中不存在则直接入栈),然后如果栈中元素数量超过限定值,则删除栈底元素(即最近最少使用的元素)。如下图:
使用
LruCache 是Android 3.1所提供的一个缓存类,可以直接使用。而 DisLruCache 目前还不是 Android SDK的一部分,但 Android 官方文档推荐使用该算法来实现硬盘缓存。
讲到 LruCache 不得不提一下 LinkedHashMap,因为 LruCache 中 Lru 算法就是通过 LinkedHashMap 来实现的。
LinkedHashMap 继承于 HashMap,使用了一个双向链表来存储 Map 中的 Entry 顺序关系,这种顺序有两种,一种是 LRU 顺序,一种是插入顺序,由其构造函数
public LinkedHashMap(int initialCapacity,float loadFactor, boolean accessOrder) |
中的最后一个参数 accessOrder 来指定。
对于get
、put
、remove
等操作,LinkedHashMap 除了要做 HashMap 做的事情,还会做些调整 Entry 顺序链表的工作。LruCache 中将 LinkedHashMap 的顺序设置为 LRU 顺序来实现 LRU 缓存,每次调用 get
(也就是从内存缓存中取图片),则将该对象移到链表的尾端。调用put
插入新的对象,则存储在链表尾端,这样当内存缓存达到设定的最大值时,将链表头部的对象(近期最少用到的)移除。
LruCache 的使用非常简单,以图片缓存为例:
int maxMemory = (int) (Runtime.getRuntime().totalMemory()/1024); |
- 设置LruCache缓存的大小,一般为当前进程可用容量的1/8。
- 重写sizeOf方法,计算出要缓存的每张图片的大小。
注意:缓存的总容量和每个缓存对象的大小所用单位要一致。
原理
LruCache 的核心思想很好理解,就是要维护一个缓存对象列表,其中对象列表的排列方式是按照访问顺序实现的,即一直没访问的对象会放在队尾,即将被淘汰。而最近访问的对象会放在队头,最后被淘汰。
这个队列由 LinkedHashMap 来维护。LinkedHashMap 由数组+双向链表的数据结构实现,其中双向链表的结构可以实现访问顺序和插入顺序,使得 LinkedHashMap 中的
对按照一定顺序排列起来。
通过下面的构造函数来指定 LinkedHashMap 中双向链表的结构是访问顺序还是插入顺序。
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { |
accessOrder 设置为 true 则为访问顺序;为 false,则为插入顺序。
以具体例子解释,当设置为true时
public static final void main(String[] args) { |
输出结果为:
0:0 |
即最近访问的最后输出,正好满足 LRU 缓存算法的思想。可见 LruCache 的巧妙实现,就是利用了LinkedHashMap 的这种数据结构。
下面我们在 LruCache 源码中具体看看,怎样应用 LinkedHashMap 来实现缓存的添加,获得和删除
构造方法
public LruCache(int maxSize) { |