当前位置:网站首页>Glide缓存核心原理详解

Glide缓存核心原理详解

2022-08-10 22:13:00 AD钙奶-lalala

Glide目前几乎是Android图片框架的不二选择,那么你知道Glide有哪些优点吗?

  1. 使用简单,链式调用( Glide.with(context).load(url).into(iv) )
  2. 占用内存较小,默认使用RGB_565,相比较Picasso内存减少一半(RGB_8888)
  3. 与activity生命周期绑定,避免内存泄漏
  4. 缓存优化

今天的主题是一起来研究一下Glide的核心原理。

Glide的三级缓存原理是什么?

读取一张图片的顺序:LRU算法缓存->弱引用缓存->磁盘缓存(如果设置了的话)。

我们的APP中想要加载某张图片时,先去LruCache中寻找图片,如果LruCache中有,则直接取出来使用,并将该图片放入WeakReference中,如果LruCache中没有,则去WeakReference中寻找,如果WeakReference中有,则从WeakReference中取出图片使用,如果WeakReference中也没有图片,则从磁盘缓存/网络中加载图片。
注:图片正在使用时存在于 activeResources 弱引用map中。

缓存图片的写入顺序:弱引用缓存->LRU算法缓存->磁盘缓存。

当图片不存在的时候,先从网络下载图片,然后将图片存入弱引用中,glide会采用一个acquired(int)变量用来记录图片被引用的次数,当acquired变量大于0的时候,说明图片正在使用中,也就是将图片放到弱引用缓存当中;如果acquired变量等于0了,说明图片已经不再被使用了,那么此时会调用方法来释放资源,首先会将缓存图片从弱引用中移除,然后再将它put到LruResourceCache当中。这样也就实现了正在使用中的图片使用弱引用来进行缓存,不再使用
中的图片使用LruCache来进行缓存的功能。

关于LRUCache:

最近最少使用算法,设定一个缓存大小,当缓存达到这个大小之后,会将最老的数据移除,避免图片占用内存过大导致OOM。LruCache 内部用LinkHashMap存取数据,在双向链表保证数据新旧顺序的前提下,设置一个最大内存,往里面put数据的时候,当数据达到最大内存的时候,将最老的数据移除掉,保证内存不超过设定的最大值。

关于LinkedHashMap:

LinkHashMap 继承HashMap,在 HashMap的基础上,新增了双向链表结构,每次访问数据的时候,会更新被访问的数据的链表指针,具体就是先在链表中删除该节点,然后添加到链表头header之前,这样就保证了链表头header节点之前的数据都是最近访问的(从链表中删除并不是真的删除数据,只是移动链表指针,数据本身在map中的位置是不变的)。

前面说了一堆,其实重点大家也很明白,就是这个LRUCache:

public class LruCache<T, Y> {
    private final LinkedHashMap<T, Y> cache = 
new LinkedHashMap<T, Y>(100, 0.75f, true);//最后一个传入true,代表基于顺序访问
    private int maxSize;
    private final int initialMaxSize;
    private int currentSize = 0;

    public LruCache(int size) {
        this.initialMaxSize = size;
        this.maxSize = size;
    }

    public void setSizeMultiplier(float multiplier) {
        if (multiplier < 0) {
            throw new IllegalArgumentException("Multiplier must be >= 0");
        }
        maxSize = Math.round(initialMaxSize * multiplier);
        evict();
    }

    protected int getSize(Y item) {
        return 1;
    }

    
    protected void onItemEvicted(T key, Y item) {
        // optional override
    }

    public int getMaxSize() {
        return maxSize;
    }

    public int getCurrentSize() {
        return currentSize;
    }


    public boolean contains(T key) {
        return cache.containsKey(key);
    }

    
    public Y get(T key) {
        return cache.get(key);
    }

    
    public Y put(T key, Y item) {
        final int itemSize = getSize(item);
        if (itemSize >= maxSize) {
            onItemEvicted(key, item);
            return null;
        }

        final Y result = cache.put(key, item);
        if (item != null) {
            currentSize += getSize(item);
        }
        if (result != null) {
            // TODO: should we call onItemEvicted here?
            currentSize -= getSize(result);
        }
        evict();

        return result;
    }

    public Y remove(T key) {
        final Y value = cache.remove(key);
        if (value != null) {
            currentSize -= getSize(value);
        }
        return value;
    }

    
    public void clearMemory() {
        trimToSize(0);
    }

    
    protected void trimToSize(int size) {
        Map.Entry<T, Y> last;
        while (currentSize > size) {
            last = cache.entrySet().iterator().next();//基于顺序访问
            final Y toRemove = last.getValue();
            currentSize -= getSize(toRemove);
            final T key = last.getKey();
            cache.remove(key);
            onItemEvicted(key, toRemove);
        }
    }

    private void evict() {
        trimToSize(maxSize);
    }
}

解释一下一个重要的成员变量maxSize是什么意思:资源的最大字节数。

我们先来分析一下put方法:

  • 首先获取资源的字节数itemSize
  • 如果itemSize直接超限制,返回,onItemEvicted方法在子类有实现,后面再看
  • cache.put将资源加到LinkedHashMap里面
  • item != null 将item的size加到currentSize上
  • result != null 说明缓存已存在,前面加到currentSize里面去的要再去掉
  • 最后调用evict方法

再来分析一下evict(意思是逐出)方法:

  • 调用trimToSize
  • 遍历获取最后一个资源,移出cache

我们再来看下LRUCache的子类来加深我们的理解:

public class LruResourceCache extends LruCache<Key, Resource<?>> 
implements MemoryCache {
    private ResourceRemovedListener listener;

    /**
     * Constructor for LruResourceCache.
     *
     * @param size The maximum size in bytes the in memory cache can use.
     */
    public LruResourceCache(int size) {
        super(size);
    }

    @Override
    public void setResourceRemovedListener(ResourceRemovedListener listener) {
        this.listener = listener;
    }

    @Override
    protected void onItemEvicted(Key key, Resource<?> item) {
        if (listener != null) {
            listener.onResourceRemoved(item);
        }
    }

    @Override
    protected int getSize(Resource<?> item) {
        return item.getSize();
    }

    @SuppressLint("InlinedApi")
    @Override
    public void trimMemory(int level) {
        if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
            // Nearing middle of list of cached background apps
            // Evict our entire bitmap cache
            clearMemory();
        } else if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
            // Entering list of cached background apps
            // Evict oldest half of our bitmap cache
            trimToSize(getCurrentSize() / 2);
        }
    }
}
  • getSize是获取资源字节数的意思
  • onItemEvicted可以理解为将资源移除

时间有点晚了,后面再用一篇文章来分析一下LinkedHashMap的数据结构,晚安,好梦!

原网站

版权声明
本文为[AD钙奶-lalala]所创,转载请带上原文链接,感谢
https://blog.csdn.net/qq_36428821/article/details/126256294