• Glide缓存核心原理详解


    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:

    1. public class LruCache {
    2. private final LinkedHashMap cache =
    3. new LinkedHashMap(100, 0.75f, true);//最后一个传入true,代表基于顺序访问
    4. private int maxSize;
    5. private final int initialMaxSize;
    6. private int currentSize = 0;
    7. public LruCache(int size) {
    8. this.initialMaxSize = size;
    9. this.maxSize = size;
    10. }
    11. public void setSizeMultiplier(float multiplier) {
    12. if (multiplier < 0) {
    13. throw new IllegalArgumentException("Multiplier must be >= 0");
    14. }
    15. maxSize = Math.round(initialMaxSize * multiplier);
    16. evict();
    17. }
    18. protected int getSize(Y item) {
    19. return 1;
    20. }
    21. protected void onItemEvicted(T key, Y item) {
    22. // optional override
    23. }
    24. public int getMaxSize() {
    25. return maxSize;
    26. }
    27. public int getCurrentSize() {
    28. return currentSize;
    29. }
    30. public boolean contains(T key) {
    31. return cache.containsKey(key);
    32. }
    33. public Y get(T key) {
    34. return cache.get(key);
    35. }
    36. public Y put(T key, Y item) {
    37. final int itemSize = getSize(item);
    38. if (itemSize >= maxSize) {
    39. onItemEvicted(key, item);
    40. return null;
    41. }
    42. final Y result = cache.put(key, item);
    43. if (item != null) {
    44. currentSize += getSize(item);
    45. }
    46. if (result != null) {
    47. // TODO: should we call onItemEvicted here?
    48. currentSize -= getSize(result);
    49. }
    50. evict();
    51. return result;
    52. }
    53. public Y remove(T key) {
    54. final Y value = cache.remove(key);
    55. if (value != null) {
    56. currentSize -= getSize(value);
    57. }
    58. return value;
    59. }
    60. public void clearMemory() {
    61. trimToSize(0);
    62. }
    63. protected void trimToSize(int size) {
    64. Map.Entry last;
    65. while (currentSize > size) {
    66. last = cache.entrySet().iterator().next();//基于顺序访问
    67. final Y toRemove = last.getValue();
    68. currentSize -= getSize(toRemove);
    69. final T key = last.getKey();
    70. cache.remove(key);
    71. onItemEvicted(key, toRemove);
    72. }
    73. }
    74. private void evict() {
    75. trimToSize(maxSize);
    76. }
    77. }

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

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

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

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

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

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

    1. public class LruResourceCache extends LruCache>
    2. implements MemoryCache {
    3. private ResourceRemovedListener listener;
    4. /**
    5. * Constructor for LruResourceCache.
    6. *
    7. * @param size The maximum size in bytes the in memory cache can use.
    8. */
    9. public LruResourceCache(int size) {
    10. super(size);
    11. }
    12. @Override
    13. public void setResourceRemovedListener(ResourceRemovedListener listener) {
    14. this.listener = listener;
    15. }
    16. @Override
    17. protected void onItemEvicted(Key key, Resource item) {
    18. if (listener != null) {
    19. listener.onResourceRemoved(item);
    20. }
    21. }
    22. @Override
    23. protected int getSize(Resource item) {
    24. return item.getSize();
    25. }
    26. @SuppressLint("InlinedApi")
    27. @Override
    28. public void trimMemory(int level) {
    29. if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
    30. // Nearing middle of list of cached background apps
    31. // Evict our entire bitmap cache
    32. clearMemory();
    33. } else if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
    34. // Entering list of cached background apps
    35. // Evict oldest half of our bitmap cache
    36. trimToSize(getCurrentSize() / 2);
    37. }
    38. }
    39. }
    • getSize是获取资源字节数的意思
    • onItemEvicted可以理解为将资源移除

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

  • 相关阅读:
    身体是革命的本钱,希望大家编程之余努力搞好身体
    vue基本使用1
    【前端】webpack5的配置及基本使用
    Hadoop伪分布式环境搭建
    互联网应用主流框架整合之SpringMVC基础组件开发
    日期时间格式化 @JsonFormat与@DateTimeFormat
    uniapp自定义顶部导航栏
    【数字电路基础】组合逻辑竞争冒险、时序逻辑亚稳态
    支持Unicode的Java正则表达式?
    自然语言处理 Paddle NLP - 检索式文本问答-理论
  • 原文地址:https://blog.csdn.net/qq_36428821/article/details/126256294