LruCache内存缓存

背景:最近,公司的项目中,需要使用大量的图片,频繁的进行bitmap的创建与销毁,内存开销太大,所以需要进行图片的缓存,经过查找相关资料,决定使用LruCache。

介绍:谷歌官方提供的一种内存缓存策略,当缓存达到最大值时,使用最近最少使用算法,剔除旧的缓存。

简单使用:

private LruCache<String, Bitmap> mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    //计算最大可用内存
    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

    //取1/8作为缓存
    final int cacheSize = maxMemory / 8;

    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            //返回,当前消耗掉的空间大小
            //注意,此处的单位要和cacheSize的单位保持一致!
            return bitmap.getByteCount() / 1024;
        }
    };
    ...
}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }
}

public Bitmap getBitmapFromMemCache(String key) {
    return mMemoryCache.get(key);
}
复制代码

源码分析:LruCache 内部使用LinkedHashMap进行数据存储,当某个value(缓存值)被命中后,该value会被移动到队列的头部。当缓存满了之后,队列尾部的value就会被移除,被移除的值,可能会被垃圾回收器进行回收(如果被缓存的value,需要主动的回收内存,则需要重写entryRemoved方法,进行主动回收);LruCache 是多线程安全的,内部自动进行了同步。

put方法分析:

/**
 * Caches {@code value} for {@code key}. The value is moved to the head of
 * the queue.
 *
 * @return the previous value mapped by {@code key}.
 */
public final V put(K key, V value) {
    //key和value不能为null!!!
    if (key == null || value == null) {
        throw new NullPointerException("key == null || value == null");
    }

    V previous;
    //进行同步
    synchronized (this) {
        putCount++;
        //size:当前已用的缓存空间,累加
        //safeSizeOf中调用的就是,我们重写的sizeOf,获取的当前值的占用空间大小
        size += safeSizeOf(key, value);
        previous = map.put(key, value);
        if (previous != null) {
            //说明是重复缓存,新的value会替换掉旧的value,需要重新计算size
            size -= safeSizeOf(key, previous);
        }
    }

    if (previous != null) {
        //false,标明,该value不是因为空间不足被清除的,如果该value占用的内存
        //需要被显示释放,则需要重写下面这个方法。
        entryRemoved(false, key, previous, value);
    }

    //重新计算size大小,保证新加入的value,不会超过maxSize,如果超过了,
    //则会移除队列尾部的value
    trimToSize(maxSize);
    return previous;
}

private int safeSizeOf(K key, V value) {
    int result = sizeOf(key, value);
    if (result < 0) {
        throw new IllegalStateException("Negative size: " + key + "=" + value);
    }
    return result;
}
复制代码

get方法分析:

/**
 * Returns the value for {@code key} if it exists in the cache or can be
 * created by {@code #create}. If a value was returned, it is moved to the
 * head of the queue. This returns null if a value is not cached and cannot
 * be created.
 */
public final V get(K key) {
    if (key == null) {
        throw new NullPointerException("key == null");
    }

    V mapValue;
    synchronized (this) {
        //从缓存中,如果找到了,直接返回
        mapValue = map.get(key);
        if (mapValue != null) {
            hitCount++;
            return mapValue;
        }
        missCount++;
    }

    /*
     * Attempt to create a value. This may take a long time, and the map
     * may be different when create() returns. If a conflicting value was
     * added to the map while create() was working, we leave that value in
     * the map and release the created value.
     */

    //缓存中,没有找到,则进行创建,该方法是个空实现,可根据需求,
    //确定是否进行创建
    V createdValue = create(key);
    if (createdValue == null) {
        return null;
    }

    //如果创建成功,则进行插入,再返回。
    synchronized (this) {
        createCount++;
        mapValue = map.put(key, createdValue);

        if (mapValue != null) {
            // There was a conflict so undo that last put
            map.put(key, mapValue);
        } else {
            size += safeSizeOf(key, createdValue);
        }
    }

    if (mapValue != null) {
        entryRemoved(false, key, createdValue, mapValue);
        return mapValue;
    } else {
        trimToSize(maxSize);
        return createdValue;
    }
}
复制代码

trimToSize方法分析:

/**
 * @param maxSize the maximum size of the cache before returning. May be -1
 *     to evict even 0-sized elements.
 */
private void trimToSize(int maxSize) {
    //直到size <= maxSzie 或者 map没有缓存了,才退出循环
    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!");
            }
            //如果,size,没有达到最大缓存值,直接结束循环
            if (size <= maxSize) {
                break;
            }
            
            //获取使用次数最少的value
            Map.Entry<K, V> toEvict = null;
            for (Map.Entry<K, V> entry : map.entrySet()) {
                toEvict = entry;
            }

            if (toEvict == null) {
                break;
            }

            key = toEvict.getKey();
            value = toEvict.getValue();
            //移除该缓存
            map.remove(key);
            //重新计算size
            size -= safeSizeOf(key, value);
            evictionCount++;
        }
        //true,表明,该value是因为缓存不足,被移除的
        entryRemoved(true, key, value, null);
    }
}
复制代码

清空所用缓存,可以直接调用evictAll()方法。

多说一句,关于bitmap是否需要显示调用recycle(),进行内存回收,看了下文档,现在是不需要显示调用的,GC会进行回收,文档如下:

//This is an advanced call, and normally need not be called, 
//since the normal GC process will free up this memory when
//there are no more references to this bitmap. 
public void recycle() {
    ...........................
    }
}
复制代码

才疏学浅,如有错误,还请指正,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Android 中使用内存缓存图片,可以使用 LruCache 或自定义实现的内存缓存。 1. LruCache LruCache 是 Android 提供的一个可以回收不常用的 Bitmap 对象的缓存类,它的大小是通过构造函数中传入的 maxsize 来指定的。 以下是使用 LruCache 的示例代码: ``` public class ImageCache { private LruCache<String, Bitmap> mMemoryCache; public ImageCache() { int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; // 可以根据需求进行调整 mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // 返回 Bitmap 对象的大小,单位为 KB return bitmap.getByteCount() / 1024; } }; } public void addToMemoryCache(String key, Bitmap bitmap) { if (getFromMemoryCache(key) == null) { mMemoryCache.put(key, bitmap); } } public Bitmap getFromMemoryCache(String key) { return mMemoryCache.get(key); } } ``` 2. 自定义实现的内存缓存 自定义实现的内存缓存可以根据需求进行调整和优化,以下是一个简单的示例代码: ``` public class ImageCache { private Map<String, Bitmap> mMemoryCache; public ImageCache() { mMemoryCache = new HashMap<String, Bitmap>(); } public void addToMemoryCache(String key, Bitmap bitmap) { if (getFromMemoryCache(key) == null) { mMemoryCache.put(key, bitmap); } } public Bitmap getFromMemoryCache(String key) { return mMemoryCache.get(key); } } ``` 使用内存缓存时,需要注意内存泄漏的问题,可以在 Activity 或 Fragment 的 onDestroy() 方法中释放缓存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值