LRU是Least Recently Used 最近最少使用算法。作为一种在内存当中存取数据的策略,LRU算法的本质是希望提高内存数据使用的命中率。核心思想就是:如果数据最近被访问过,那么将来被访问的几率也会更高,在LRU算法中,最近使用过的数据总是在队列的前面,总是淘汰处于队列中最末尾的数据。
现在就来看看在Picasso中LruCache是如何实现的。
Picasso中LruCache有两个构造方法:
如果没有指定内存大小,就通过Utils方法里提供的计算内存的方法来指定:
public LruCache(Context context) {
this(Utils.calculateMemoryCacheSize(context));
}
static int calculateMemoryCacheSize(Context context) {
ActivityManager am = (ActivityManager)getService(context, "activity");
boolean largeHeap = (context.getApplicationInfo().flags & 1048576) != 0;//当前是debug模式还是release模式
int memoryClass = am.getMemoryClass();//每个应用可用的最大内存,即heapgrowthlimit值
if(largeHeap && VERSION.SDK_INT >= 11) {
memoryClass = am.getLargeMemoryClass();//16M
}
//memoryClass单位是M,*1048576转字节
return 1048576 * memoryClass / 7;//当前应用内存的1/7
}
第二种构造方法:指定了内存大小
public LruCache(int maxSize) {
if(maxSize <= 0) {
throw new IllegalArgumentException("Max size must be positive.");
} else {
this.maxSize = maxSize;
this.map = new LinkedHashMap(0, 0.75F, true);// 初始化了一个LinkedMap成员变量用来存储图片
}
}
粗略的介绍下LinkedHashMap传入的三个参数:
public LinkedHashMap(
int initialCapacity, float loadFactor, boolean accessOrder) {
…
}
第一个参数initialCapacity是初始化空间大小,第二个参数loadFactor是加载因子,就是当他的size大于initialCapacity*loadFactor的时候就会扩容,他的默认值是0.75(0.75这个值是权衡时间与空间利弊以后的一个最佳值,如果高于这个值,会节省一些空间,但会影响时间效率), 第三个参数accessOrder为true,则是按照最常访问来进行排序,如果为false,则是按照插入时间来进行排序
再来看看核心的set方法
public void set(String key, Bitmap bitmap) {
if(key != null && bitmap != null) {
//同步代码块,保证多线程加载图片情况下的线程安全
synchronized(this) {
++this.putCount;
this.size += Utils.getBitmapBytes(bitmap);
Bitmap previous = (Bitmap)this.map.put(key, bitmap);//把bitmap放到LinkedHashmap中,put方法中注释:@return the value of any previous mapping with the specified key or {@code null} if there was no such mapping. (如果以前put过,返回key对应的value,如果没有返回null )
if(previous != null) {//存在同样key的value时,从新计算size
this.size -= Utils.getBitmapBytes(previous);
}
}
this.trimToSize(this.maxSize);
} else {
throw new NullPointerException("key == null || bitmap == null");
}
}
private void trimToSize(int maxSize) {
while(true) {
synchronized(this) {
if(this.size >= 0 && (!this.map.isEmpty() || this.size == 0)) {
if(this.size > maxSize && !this.map.isEmpty()) {//如果size>maxSize且map不为空,进行删除图片的操作
Entry toEvict = (Entry)this.map.entrySet().iterator().next();//header.next对应的节点,也就是最近最不常使用的那个节点
String key = (String)toEvict.getKey();
Bitmap value = (Bitmap)toEvict.getValue();
this.map.remove(key);
this.size -= Utils.getBitmapBytes(value);
++this.evictionCount;
continue;//如果size还是>maxSize,继续循环
}
return;
}
throw new IllegalStateException(this.getClass().getName() + ".sizeOf() is reporting inconsistent results!");
}
}
}
简单介绍下LinkedHashMap的双向链表结构

public LinkedHashMap(
int initialCapacity, float loadFactor, boolean accessOrder) {
super(initialCapacity, loadFactor);
init();
this.accessOrder = accessOrder;
}
@Override void init() {
header = new LinkedEntry<K, V>();
}
初始化了一个header,这个header是双向环形链表的头,不存放任何数据的,我们看到上面的注释,意思是第一个真正的entry是header的下一个,最后一个entry是header的前一个,如果为null,则他的前一个和后一个都等于header,因为他是个环形链表,所以header的下一个也是最先加入的,header的前一个是最后一个加入的
网友评论