在android开发中我们为了提升App的性能,常用到缓存策略,特别在图片加载中,为了尽可能减少网络请求,通常会采用内存缓存和硬盘缓存的对已经加载的图片进行存储,但是由于磁盘的不能能是无限大的,这就要求我们需要对存储进行必要的处理,保证存储大小在一个合理的范围内,本章小编就LRU的内存缓存策略进行分析,为什么能够实现LRU的:

什么是LRU算法呢?

LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
整体来说,这个算法,就是按照这三点来的

  1. 新数据插入到链表头部;
  2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
  3. 当链表满的时候,将链表尾部的数据丢弃。

由于LRU算法涉及到Map集合 首先我们介绍一下:HashMap和LinkedHashMap的区别:

HashMap是一个最常用的Map,它根据键的hashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为NULL,允许多条记录的值为NULL。
HashMap不支持线程同步,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致性。如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力。
Hashtable与HashMap类似,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtable在写入时会比较慢。
LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的。

下面是小编分析的ImageLoader中的LruMemoryCache的源码:不足之处请各位大神指点:

public class LruMemoryCache implements MemoryCacheAware<String,Bitmap>{

    private final LinkedHashMap<String, Bitmap> map;
    private final int maxSize;//定义缓存的大小
    /** Size of this cache in bytes */
    private int size;
    public LruMemoryCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0"); //不能小于零
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);//初始化一个LinkedHashMap扩展音质0.75
    }
    //删除最旧的条目,直到剩余项的总数小于或小于所请求的大小为止。
    private void trimToSize(int maxSize) {
        while (true) {
            String key;
            Bitmap value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");
                }
                if (size <= maxSize || map.isEmpty()) {
                    break;
                }
                Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next(); //查找最后一个的方法
                if (toEvict == null) {
                    break;
                }
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);//移除最后一个值
                size -= sizeOf(key, value);//计算大小
            }
        }
    }

   //计算bitmap的大小
    private int sizeOf(String key, Bitmap value) {
            return value.getRowBytes() * value.getHeight();
    }

    @Override
    public boolean put(String key, Bitmap value) {

        if (key==null||value==null){
            throw new NullPointerException("key == null || value == null");
        }

        synchronized (this){
            size+=sizeOf(key, value);
            Bitmap prev = map.put(key, value);  
            if (prev!=null){
                size-=sizeOf(key, value);
            }   //判断是否已经存在,如果pre不等于空 说明已存在,则减去一个大小,并且插入到链表头部
        }
        trimToSize(maxSize);
        return true;
    }
    @Override
    public Bitmap get(String key) {
        if (key==null){
            throw new NullPointerException("key == null || value == null");
        }
        synchronized (this){
            return  map.get(key);
        }
    }

    @Override
    public void remove(String key) {
            if (key == null) {
                throw new NullPointerException("key == null");
            }
            synchronized (this) {
                Bitmap previous = map.remove(key);
                if (previous != null) {
                    size -= sizeOf(key, previous);
                }
            }
    }
    @Override
    public Collection<String> keys() {
        synchronized (this) {
            return new HashSet<String>(map.keySet());
        }
    }
   //清除所有的值
    @Override
    public void clear() {
         trimToSize(-1);
    }

我们可以看到这个类中维护的是一个LinkedHashMap,在LruMemoryCache构造函数中我们可以看到,我们为其设置了一个缓存图片的最大值maxSize,并实例化LinkedHashMap, 而从LinkedHashMap构造函数的第三个参数为ture,表示它是按照访问顺序进行排序的,
我们来看将bitmap加入到LruMemoryCache的方法put(String key, Bitmap value), sizeOf()是计算每张图片所占的byte数,size是记录当前缓存bitmap的总大小,如果该key之前就缓存了bitmap,我们需要将之前的bitmap减掉去,接下来看trimToSize()方法,如果当前缓存的bitmap总数小于设定值maxSize,不做任何处理,如果当前缓存的bitmap总数大于maxSize,删除LinkedHashMap中的第一个元素,size中减去该bitmap对应的byte数我们可以看到该缓存类比较简单,逻辑也比较清晰