美文网首页
Picasso--LruCache

Picasso--LruCache

作者: Qi0907 | 来源:发表于2018-05-16 14:05 被阅读0次

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的双向链表结构


image.png
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的前一个是最后一个加入的

相关文章

  • Picasso--LruCache

    LRU是Least Recently Used 最近最少使用算法。作为一种在内存当中存取数据的策略,LRU算法的本...

网友评论

      本文标题:Picasso--LruCache

      本文链接:https://www.haomeiwen.com/subject/vmueuxtx.html