Android 的缓存机制 Lrucache

首先在Android的三级缓存中,其中主要的就是内存缓存和硬盘缓存。这两种缓存机制的实现都应用到了LruCache算法,今天我们就从使用到源码解析,来彻底理解Android中的缓存机制。 

      Android中缓存策略主要包含缓存的添加、获取和删除这三类操作。如何添加和获取缓存这个比较好理解,那么为什么还要删除缓存呢?这是因为不管是内存缓存还是硬盘缓存,它们的缓存大小都是有限的。当缓存满了之后,再想其添加缓存,这个时候就需要删除一些旧的缓存并添加新的缓存。

因此LRU(Least Recently Used)缓存算法便应运而生,LRU是近期最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。采用LRU算法的缓存有两种:LrhCache和DisLruCache,分别用于实现内存缓存和硬盘缓存,其核心思想都是LRU缓存算法。

      LruCache是Android 3.1所提供的一个缓存类,所以在Android中可以直接使用LruCache实现内存缓存。而DisLruCache目前在Android 还不是Android SDK的一部分,但是Android官方文档推荐使用该算法来实现硬盘缓存。 

    LruCache是个泛型类,主要算法原理是把最近使用的对象用强引用(即我们平常使用的对象引用方式)存储在 LinkedHashMap 中。当缓存满时,把最近最少使用的对象从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作。


Lrucahce的使用

1 .设置LruCache缓存的大小,一般为当前进程可用容量的1/8。
2  重写sizeOf方法,计算出要缓存的每张图片的大小。

请注意:缓存的总容量和每个缓存对象的大小所用单位要一致。

LruCache的核心思想很好理解,就是要维护一个缓存对象列表,其中对象列表的排列方式是按照访问顺序实现的,即一直没访问的对象,将放在队尾,即将被淘汰。而最近访问的对象将放在队头,最后被淘汰。


 这个队列是由LinkedHashMap来维护。

而LinkedHashMap是由数组+双向链表的数据结构来实现的。其中双向链表的结构可以实现访问顺序和插入顺序,使得LinkedHashMap中的<key,value>对按照一定顺序排列起来。

通过下面构造函数来指定LinkedHashMap中双向链表的结构是访问顺序还是插入顺序。

public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
  super(initialCapacity, loadFactor); 
  this.accessOrder = accessOrder; }

当accessOrder设置为true则为访问顺序,为false,则为插入顺序。

put()方法
public final V put(K key, V value) {
  //不可为空,否则抛出异常
if (key == null || value == null) { 
  throw new NullPointerException( "key == null || value == null");
 }
 V previous; synchronized ( this) {
  //插入的缓存对象值加1 
 putCount++;
  //增加已有缓存的大小 
 size += safeSizeOf(key, value); 
  //向map中加入缓存对象 
 previous = map.put(key, value);
  //如果已有缓存对象,则缓存大小恢复到之前
if (previous != null) { size -= safeSizeOf(key, previous); } }
  //entryRemoved()是个空方法,可以自行实现
if (previous != null) {
 entryRemoved( false, key, previous, value); } 
  //调整缓存大小(调用的重点)
trimToSize(maxSize);
  return previous; }
调用 trimToSize()方法,来判断缓存是否已满,如果满了就要删除近期最少使用的算法。

public void trimToSize(int maxSize)
  //死循环 
  while ( true) { 
 K key;
 V value; 
 synchronized ( this) { 
  //如果map为空并且缓存size不等于0或者缓存size小于0,抛出异常 
  if (size < 0 || ( map.isEmpty() && size != 0)) { 
  throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!"); 
 } 
  //如果缓存大小size小于最大缓存,或者map为空,不需要再删除缓存对象,跳出循环
if (size <= maxSize || map.isEmpty()) {
  break
 } 
  //迭代器获取第一个对象,即队尾的元素,近期最少访问的元素 
 Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
 key = toEvict.getKey();
 value = toEvict.getValue();
  //删除该对象,并更新缓存大小 
  map.remove(key);
 size -= safeSizeOf(key, value); 
 evictionCount++; 
 }
 entryRemoved( true, key, value, null); 
 } 
 }