当前位置: 移动技术网 > 移动技术>移动开发>Android > 通过LruCache防止Android OOM

通过LruCache防止Android OOM

2020年07月17日  | 移动技术网移动技术  | 我要评论

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网