欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

通过LruCache防止Android OOM

程序员文章站 2022-03-28 17:53:43
LruCache位于android.util.LruCache,用来保证固定长度的Map缓存,如果缓存满了的话,会移除掉最早访问的key-value。在Android中,Bitmap是很占内存的,所以可以考虑使用LruCache。用法如下:private static LruCache mCache;static { mCache = new LruCache(CACHE_MAX_SIZE) {...

LruCache位于android.util.LruCache,用来保证固定长度的Map缓存,如果缓存满了的话,会移除掉最早访问的key-value。在Android中,Bitmap是很占内存的,所以可以考虑使用LruCache。用法如下:

private static LruCache<String, Bitmap> mCache;
static {
    mCache = new LruCache<String, Bitmap>(CACHE_MAX_SIZE) {
        @Override
        protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
            if (oldValue != newValue)//连续put相同key的情况,这种情况也可能导致Bitmap一直增加,所以要把旧的bitmap回收
                oldValue.recycle();
        }
    };
}

 

我们来看一下LruCache的源码。

public void resize(int maxSize) {
    if (maxSize <= 0) {
        throw new IllegalArgumentException("maxSize <= 0");
    }

    synchronized (this) { //这里考虑了线程安全
        this.maxSize = maxSize;
    }
    trimToSize(maxSize);//裁剪到指定大小
}
private 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;
            }

            // BEGIN LAYOUTLIB CHANGE
            // get the last item in the linked list.
            // This is not efficient, the goal here is to minimize the changes
            // compared to the platform version.
            Map.Entry<K, V> toEvict = null;//要逐出的实体
            for (Map.Entry<K, V> entry : map.entrySet()) {
                toEvict = entry;
            }
            // END LAYOUTLIB CHANGE

            if (toEvict == null) {
                break;
            }

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

        entryRemoved(true, key, value, null);
    }
}
/**
 * Called after a cache miss to compute a value for the corresponding key.
 * Returns the computed value or null if no value can be computed. The
 * default implementation returns null.
 *
 * <p>The method is called without synchronization: other threads may
 * access the cache while this method is executing.
 *
 * <p>If a value for {@code key} exists in the cache when this method
 * returns, the created value will be released with {@link #entryRemoved}
 * and discarded. This can occur when multiple threads request the same key
 * at the same time (causing multiple values to be created), or when one
 * thread calls {@link #put} while another is creating a value for the same
 * key.
 */
protected V create(K key) {
    return null;
}

这个方法不是线程安全的,在没有通过key拿到value时,会默认创建一个null返回,你也可以通过继承重写create()方法,计算key的默认value。

/**
 * @param maxSize for caches that do not override {@link #sizeOf}, this is
 *     the maximum number of entries in the cache. For all other caches,
 *     this is the maximum sum of the sizes of the entries in this cache.
 */
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);//第三个参数为accessOrder,true代表按key的访问顺序排序,false代表按key的插入顺序排序
}

由于它内部使用了一个LinkedHashMap,给定了accessOrder一个true,所以最早的key-value会在缓存满后被移除。其他put和get都是基于LinkedHashMap的,增加了几个变量的保存,下面我们看下LruCache新增的几个变量。

    private int size;             //当前存储的大小
    private int maxSize;         //允许存储的最大缓存数量
    private int putCount;       //put的次数
    private int createCount;    //create的次数
    private int evictionCount;  //逐出的次数
    private int hitCount;       //通过get取到value的次数
    private int missCount;      //通过get没有取到value的次数

本文地址:https://blog.csdn.net/a_lwh____/article/details/107379992

相关标签: Android应用开发