介绍
LruCache
是Android3.1提供的一个缓存类,用于数据缓存,一般用于图片的内存缓存。Lru的英文是Least Recently Used
,也就是近期最少使用算法,核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。
当我们进行网络加载图片的时候,肯定要对图片进行缓存,这样下次加载图片就可以直接从缓存中取。三级缓存
大家应该都比较熟悉,内存,硬盘和网络。所以一般要进行内存缓存和硬盘缓存,其中内存缓存就是用的LruCache。
使用
public class MyImageLoader {
private LruCache mLruCache;public MyImageLoader() {int maxMemory = (int) (Runtime.getRuntime().maxMemory())/1024;int cacheSize = maxMemory / 8;
mLruCache = new LruCache(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getRowBytes()*value.getHeight()/1024;
}
};
}/**
* 添加图片缓存
*/public void addBitmap(String key, Bitmap bitmap) {
mLruCache.put(key, bitmap);
}/**
* 从缓存中获取图片
*
*/public Bitmap getBitmap(String key) {return mLruCache.get(key);
}
}
public class MyImageLoader {
private LruCache mLruCache;public MyImageLoader() {int maxMemory = (int) (Runtime.getRuntime().maxMemory())/1024;int cacheSize = maxMemory / 8;
mLruCache = new LruCache(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getRowBytes()*value.getHeight()/1024;
}
};
}/**
* 添加图片缓存
*/public void addBitmap(String key, Bitmap bitmap) {
mLruCache.put(key, bitmap);
}/**
* 从缓存中获取图片
*
*/public Bitmap getBitmap(String key) {return mLruCache.get(key);
}
}
使用方法如上,只需要提供缓存的总容量大小并重写sizeOf
方法计算缓存对象大小即可。这里总容量的大小也是通用方法,即进程可用内存的1/8,单位kb。然后就可以使用put方法来添加缓存对象,get方法来获取缓存对象。
原理
原理其实也很简单,就是用到了LRU
算法,内部使用LinkedHashMap
进行存储。在缓存满了之后,会将最近最少使用的元素移除。怎么保证找到这个最近最少的元素呢?就是每次使用get方法访问了元素或者增加了一个元素,就把元素移动到LinkedHashMap
的尾部,这样第一个元素就是最不经常使用的元素,在容量满了之后就可以将它移除。
简单看看源码:
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap(0, 0.75f, true);
}public final V put(K key, V value) {if (key == null || value == null) {throw new NullPointerException("key == null || value == null");
}
V previous; //查找是否已经存在key对应的元素synchronized (this) {
putCount++;//计算entry的大小
size += safeSizeOf(key, value);
previous = map.put(key, value);if (previous != null) {//如果之前存在,这先减去之前那个entry所占用的内存大小
size -= safeSizeOf(key, previous);
}
}if (previous != null) {//如果之前存在则调用entryRemoved回调子类重写的此方法,做一些处理
entryRemoved(false, key, previous, value);
}//根据最大的容量,计算是否需要淘汰掉最不常使用的entry
trimToSize(maxSize);return previous;
}public final V get(K key) {if (key == null) {throw new NullPointerException("key == null");
}
V mapValue;//根据key来查询符合条件的etnrysynchronized (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返回的是已经存在相同key的entry
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;
}
}
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap(0, 0.75f, true);
}public final V put(K key, V value) {if (key == null || value == null) {throw new NullPointerException("key == null || value == null");
}
V previous; //查找是否已经存在key对应的元素synchronized (this) {
putCount++;//计算entry的大小
size += safeSizeOf(key, value);
previous = map.put(key, value);if (previous != null) {//如果之前存在,这先减去之前那个entry所占用的内存大小
size -= safeSizeOf(key, previous);
}
}if (previous != null) {//如果之前存在则调用entryRemoved回调子类重写的此方法,做一些处理
entryRemoved(false, key, previous, value);
}//根据最大的容量,计算是否需要淘汰掉最不常使用的entry
trimToSize(maxSize);return previous;
}public final V get(K key) {if (key == null) {throw new NullPointerException("key == null");
}
V mapValue;//根据key来查询符合条件的etnrysynchronized (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返回的是已经存在相同key的entry
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;
}
}
其实可以看到,LruCache
类本身做的事情不多,限定了缓存map的大小,然后利用了LinkHashMap完成了LRU的缓存策略。所以主要的实现LRU逻辑部分还是在LinkHashMap中。LinkedHashMap
是hashmap
和链表
的结合体,通过链表来记录元素的顺序和链接关系,通过HashMap来存储数据,它可以控制元素的被遍历时候输出的顺序。他是一个双向链表,上面说过他会把最近访问的元素放到队列的尾部,有兴趣的可以看看LinkHashMap
的源码。