LRU缓存策略

140 阅读2分钟

LRU缓存策略

LRU((Least Recently Used)是最近最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些最近最少使用的缓存对象。采用LRU算法的缓存有两种:LrhCache和DiskLruCache

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

api介绍

LruCache是一个泛型类,需要指定相应的k和v

构造方法

public LruCache(int maxSize) {
    if (maxSize <= 0) {
        throw new IllegalArgumentException("maxSize <= 0");
    }
    this.maxSize = maxSize;
    this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}

使用LinkedHashMap来存放缓存列表,LinkedHashMap是双向链表结构,里面的数据是有序,可以是插入的顺序或者访问的顺序

put

public final V put(K key, V value) {
    if (key == null || value == null) {
        throw new NullPointerException("key == null || value == null");
    }

    ...
    synchronized (this) {
        putCount++;
        size += safeSizeOf(key, value);
        previous = map.put(key, value);
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }
    ...
}
  • key和value都不能为空
  • 通过使用synchronized来实现线程安全
  • sizeof返回每个缓存的大小

size

protected int sizeOf(K key, V value) {
    return 1;
}

使用的时候需要重载这个方法来指明每个缓存的大小

trimToSize-控制大小

public void trimToSize(int maxSize) {
    while (true) {
        K key;
        V value;
        synchronized (this) {
            if (size < 0 || (map.isEmpty() && size != 0)) {
                throw new IllegalStateException(getClass().getName()
                        + ".sizeOf() is reporting inconsistent results!");
            }

            if (size <= maxSize) {
                break;
            }

            Map.Entry<K, V> toEvict = map.eldest();
            if (toEvict == null) {
                break;
            }

            key = toEvict.getKey();
            value = toEvict.getValue();
            map.remove(key);
            size -= safeSizeOf(key, value);
            evictionCount++;
        }

        entryRemoved(true, key, value, null);
    }
}

每次调用put方法之后会调用这个方法控制缓存的大小,如果超过了maxSize,会移除掉最后一个

使用

创建一个4M的存放bitmap的缓存

val cacheSize = 4*1024*1024 //4MiB
val bitmapCache = object: LruCache<String, Bitmap>(cacheSize){
    override fun sizeOf(key: String, value: Bitmap): Int {
        return value.byteCount
    }
    
    override fun entryRemoved(evicted: Boolean, key: String?, oldValue: Bitmap?, newValue: Bitmap?) {
        super.entryRemoved(evicted, key, oldValue, newValue)

        Log.d("tag", "remove:"+key)
    }

}

创建bitmap,添加到相应的缓存中

//使用Config.RGB_565,一个像素占两个字节,总共500,000个字节
val bitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.RGB_565)

for (item in (0..9)){
    Log.d("tag", "index:"+item)
    bitmapCache.put(item.toString(), bitmap)

}

打印

D/tag: index:0
D/tag: index:1
D/tag: index:2
D/tag: index:3
D/tag: index:4
D/tag: index:5
D/tag: index:6
D/tag: index:7
D/tag: index:8
D/tag: remove:0
D/tag: index:9
D/tag: remove:1

添加第9的时候,已经超过缓存,会移除掉最后一个,然后会在entryRemoved方法回调