前言

LruCache是什么?

最初我也不知道去缓存请求到的数据,直接都是通过http请求,根据得到的返回数据展示到界面上,网络不好时要等待好久请求才能成功,或者干脆就失败了,这样给用户的体验是很不好的。之后在学习中,了解到了Android SDK中有处理缓存的LruCache,这个LruCache是在android.util包下的,是API level 12引入的,对于API level 12之前的系统可以使用support library v4中的LruCache。

  1. LruCache是一个泛型类。
  2. LRU是Least Recently Used的缩写,即“最近最少使用”,说明LRU缓存算法的淘汰策略是把最近最少使用的数据移除,让出内存给最新读取的数据。
  3. 它采用的是内存缓存数据,它内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象。
  4. 它是线程安全的。
  5. 其中提供了put、get、remove方法来完成缓存的添加、获取和移除操作。
  6. 每次添加的缓存对象会置顶,当缓存满时,LruCache会移除较早使用的缓存对象,然后再添加新的缓存对象。

通常我们需要用到的缓存策略,简单的来说是将从网络请求得到的数据先缓存到内存中,等到下次再请求的时候,先从缓存中去获取,如果缓存中有,就将缓存数据取出;如果没有,则再从网络上请求,这是一种基本的缓存策略。

LruCache(Least Recently Used Cache)是 Android 提供的内存缓存工具类,适合缓存 Bitmap、字符串等对象。

特点

  • 基于 LinkedHashMap 实现。
  • 自动移除最近最少使用的条目。
  • 线程安全。

使用步骤

  1. 继承 LruCache<K, V> 或直接实例化。
  2. 重写 sizeOf() 方法(可选,默认返回 1)。
  3. 设置最大缓存大小(通常为可用内存的 1/8)。

如何使用LruCache?

我将以ImageLoader 项目为例说明LruCache的使用方法。

由于support v4包中的LruCache可以用于API level 12之前的系统,和android.util包的LruCache的区别是在trimToSize中获取将要删除元素的方法不一样:

1
2
3
4
5
android.util.LruCache
Map.Entry<K, V> toEvict = map.eldest();

android.support.v4.util.LruCache
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();

LinkedHashMap的eldest()方法已经被标注为@hide,所以考虑到兼容性的问题,建议使用android.support.v4.util.LruCache。

(1) 定义LruCache对象的引用,并且实例化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 利用Runtime运行时来获取最大的内存大小
*/
public ImageLoader() {
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8; //官方推荐1/8,可根据要缓存的图片大小进行调整
mCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
Log.d(TAG, "sizeOf: "+value.getByteCount());
return value.getByteCount();
//也可以return value.getRowBytes() * value.getHeight();
}
};
}

注意: 由源码可知在safeSizeOf 方法中有调用sizeOf ,默认返回1,这显然不合理,所以我们肯定是需要重写的,将value的大小作为每个item的size。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private int safeSizeOf(K key, V value) {
int result = sizeOf(key, value);
if (result < 0) {
throw new IllegalStateException("Negative size: " + key + "=" + value);
}
return result;
}

/**
* Returns the size of the entry for {@code key} and {@code value} in
* user-defined units. The default implementation returns 1 so that size
* is the number of entries and max size is the maximum number of entries.
*
* <p>An entry's size must not change while it is in the cache.
*/
protected int sizeOf(K key, V value) {
return 1;
}

(2) 添加缓存对象

在得到LruCache对象的实例后,可以通过put(String key, Bitmap value) 方法,将对象添加进去。

1
2
3
4
5
6
7
8
9
10
/**
* 将图片添加到缓存中,以url为key,以bitmap为value;
* @param url
* @param bitmap
*/
public void addBitmapToCache(String url, Bitmap bitmap) {
if (getBitmapFromCache(url) == null) {
mCache.put(url, bitmap);
}
}

(3) 获取缓存对象

通过get(String key) 方法,得到对于的缓存对象。

1
2
3
4
5
6
7
8
/**
* 从缓存中获取图片
* @param url
* @return
*/
public Bitmap getBitmapFromCache(String url) {
return !TextUtils.isEmpty(url) ? mCache.get(url) : null;
}

(4) 删除缓存对象

1
2
3
4
5
6
7
8
9
/**
* 从缓存中删除图片
* @param url
*/
public void removeBitmapFromCache(String url) {
if (!TextUtils.isEmpty(url)) {
mCache.remove(url);
}
}

关于android.util.LruCache源码解析

总结

从使用上来看,LruCache并不复杂,map的操作很容易看懂,我们在项目中可以多使用这些缓存策略,会提升我们APP的用户体验,重点还有节省流量哦!