首先在Android的三级缓存中,其中主要的就是内存缓存和硬盘缓存。这两种缓存机制的实现都应用到了LruCache算法,今天我们就从使用到源码解析,来彻底理解Android中的缓存机制。
Android中缓存策略主要包含缓存的添加、获取和删除这三类操作。如何添加和获取缓存这个比较好理解,那么为什么还要删除缓存呢?这是因为不管是内存缓存还是硬盘缓存,它们的缓存大小都是有限的。当缓存满了之后,再想其添加缓存,这个时候就需要删除一些旧的缓存并添加新的缓存。
因此LRU(Least Recently Used)缓存算法便应运而生,LRU是近期最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。采用LRU算法的缓存有两种:LrhCache和DisLruCache,分别用于实现内存缓存和硬盘缓存,其核心思想都是LRU缓存算法。
LruCache是Android 3.1所提供的一个缓存类,所以在Android中可以直接使用LruCache实现内存缓存。而DisLruCache目前在Android 还不是Android SDK的一部分,但是Android官方文档推荐使用该算法来实现硬盘缓存。
LruCache是个泛型类,主要算法原理是把最近使用的对象用强引用(即我们平常使用的对象引用方式)存储在 LinkedHashMap 中。当缓存满时,把最近最少使用的对象从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作。
Lrucahce的使用
1 .设置LruCache缓存的大小,一般为当前进程可用容量的1/8。
2 重写sizeOf方法,计算出要缓存的每张图片的大小。
请注意:缓存的总容量和每个缓存对象的大小所用单位要一致。
LruCache的核心思想很好理解,就是要维护一个缓存对象列表,其中对象列表的排列方式是按照访问顺序实现的,即一直没访问的对象,将放在队尾,即将被淘汰。而最近访问的对象将放在队头,最后被淘汰。
这个队列是由LinkedHashMap来维护。
而LinkedHashMap是由数组+双向链表的数据结构来实现的。其中双向链表的结构可以实现访问顺序和插入顺序,使得LinkedHashMap中的<key,value>对按照一定顺序排列起来。
通过下面构造函数来指定LinkedHashMap中双向链表的结构是访问顺序还是插入顺序。
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder; }
当accessOrder设置为true则为访问顺序,为false,则为插入顺序。
put()方法
public final V put(K key, V value) {
if (key ==
null || value ==
null) {
throw
new NullPointerException(
"key == null || value == null");
}
V previous;
synchronized (
this) {
putCount++;
size += safeSizeOf(key, value);
previous = map.put(key, value);
if (previous !=
null) { size -= safeSizeOf(key, previous); } }
if (previous !=
null) {
entryRemoved(
false, key, previous, value); }
trimToSize(maxSize);
return previous; }
调用 trimToSize()方法,来判断缓存是否已满,如果满了就要删除近期最少使用的算法。
public 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 ||
map.isEmpty()) {
break;
}
Map.Entry<K, V> toEvict =
map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(
true, key, value, null);
}
}