Glide看点(三) 内存管理

栏目: IOS · Android · 发布时间: 5年前

内容简介:Glide里的缓存是一个精妙的多级缓存,从文档里我们可以知道总的缓存策略如下:本文讲分析Glide对于Bitmap的内存缓存逻辑:Glide里有内存里分别有两层缓存,一个是Active Cache一个是普通的Cache。他们本质上都是LinkedHashMap实现的LRUCache,只是调度策略让他们拥有了不同的功能。

Glide看点(三) 内存管理

Glide里的缓存是一个精妙的多级缓存,从文档里我们可以知道总的缓存策略如下:

默认情况下,Glide 会在开始一个新的图片请求之前检查以下多级的缓存:

活动资源 (Active Resources) – 现在是否有另一个 View 正在展示这张图片?

内存缓存 (Memory cache) – 该图片是否最近被加载过并仍存在于内存中?

资源类型(Resource) – 该图片是否之前曾被解码、转换并写入过磁盘缓存?

数据来源 (Data) – 构建这个图片的资源是否之前曾被写入过文件缓存?

前两步检查图片是否在内存中,如果是则直接返回图片。后两步则检查图片是否在磁盘上,以便快速但异步地返回图片。

如果四个步骤都未能找到图片,则Glide会返回到原始资源以取回数据(原始文件,Uri, Url等)。

本文讲分析Glide对于Bitmap的内存缓存逻辑:

ActiveCache的使用

Glide里有内存里分别有两层缓存,一个是Active Cache一个是普通的Cache。他们本质上都是LinkedHashMap实现的LRUCache,只是调度策略让他们拥有了不同的功能。

从load方法来看策略:

EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      return null;
    }

    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      return null;
    }
  • 加载时先从Active Cache里检查,如果没有再从普通的Cache里拿去
  • 当从Normal cache里取出来后会把对象加入到Activie里,并且从普通的cache里移除
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
      return null;
    }

    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
      cached.acquire();
      activeResources.activate(key, cached);
    }
    return cached;
  }
private EngineResource<?> getEngineResourceFromCache(Key key) {
    Resource<?> cached = cache.remove(key);

    final EngineResource<?> result;
    if (cached == null) {
      result = null;
    } else if (cached instanceof EngineResource) {
      // Save an object allocation if we've cached an EngineResource (the typical case).
      result = (EngineResource<?>) cached;
    } else {
      result = new EngineResource<>(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/);
    }
    return result;
  }
  • 当resource release的时候会在active resouce移除对象,在普通cache里加入
@Override
  public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    Util.assertMainThread();
    activeResources.deactivate(cacheKey);
    if (resource.isCacheable()) {
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource);
    }
  }

画个数据流图看得更直观一些

Glide看点(三) 内存管理

注: resource release时机解释:每个资源有一个自己的使用计数,每当使用的时候会调用acquire方法给计数加1,每次job完成后会计数减1,当计数为零时进行release

Object Pool的使用

我们知道如果较短时间内频繁的新建对象是会造成内存的抖动,可能造成界面的卡顿。

Glide看点(三) 内存管理

Object Pool就是为了避免这种情况发生的一种常见设计模式。它会初始化一些列的对象,当需要对象的时候不是去创建一个新的对象,二手去复用闲置的对象资源。当没有闲置资源的时候进行扩容。

Glide里大量使用了Object Pool的实现。

Bitmap Pool

Bitmap Pool是保证Bitmap回收使用管理的类,避免了Bitmap被大量创建造成频繁GC的问题。

下面已LRUBitmapPool为安利分析

LRUBitmapPool持有LruPoolStrategy(具体实现是SizeStrategy,SizeConfigStrategy),它是正常持有Bitmap缓存的地方

com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool


  private final KeyPool keyPool = new KeyPool();

  private final GroupedLinkedMap<Key, Bitmap> groupedMap = new GroupedLinkedMap<>();

  private final NavigableMap<Integer, Integer> sortedSizes = new PrettyPrintTreeMap<>();

Key 根据width和height算出实际的bitmap大小的一个类,作为HashMap的键 (后面会解释为什么Key是bitmap大小)

KeyPool是用宽高生成一个Key的ObjectPool

groupedMap: 缓存Bitmap的对象,是一个修改过的LinkedHashMap作为LRU cache

sortedSizes: 记录每个Key下对应的Bitmap的数量,且已大小排序

为什么Bitmap Pool的Key是Bitmap的尺寸

Android里默认的API每次decode bitmap或则一些对bitmap的操作时默认是新创建一张Bitmap。API 11以后提供了BitmapFactory.Options.inBitmap这个变量来告诉系统去复用原来的已经存在的bitmap而不是重新创建bitmap从而达到节省内存的目的。

但是inBitmap的使用有一些限制,复用的bitmap的大小必须大于等于原来的尺寸。所以需要用bitmap的尺寸作为Key来缓存bitmap。

Glide看点(三) 内存管理

其他Tips

  • 不同的BitMapConfig不能复用,所以需要用不同的Pool来缓存bitmap。这里也就是Glide里的

    com/bumptech/glide/load/engine/bitmap_recycle/SizeConfigStrategy.java 存在的意义

    Glide看点(三) 内存管理

总结

  • 从Glide里我们可以学习到如何用LruCache建立多级缓存的逻辑。
  • 如何使用inBitmap对bitmap进行缓存
  • LinkedHashMap是如何实现LRU管理的(双向循环链表)
    ## 参考
    Glide v4 Caching
    Re-using Bitmaps

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

若为自由故

若为自由故

[美] Sam Williams / 邓楠、李凡希 / 人民邮电出版社 / 2015-4 / 49

理查德·马修·斯托曼(Richard Matthew Stallman,简称RMS)是自由软件之父,他是自由软件运动的精神领袖、GNU计划以及自由软件基金会的创立者。作为一个著名的黑客,他的主要成就包括Emacs及后来的GNU Emacs、GNU C 编译器及GDB 调试器。他编写的GNU通用公共许可证(GNU GPL)是世上最广为采用的自由软件许可证,为copyleft观念开拓出一条崭新的道路。......一起来看看 《若为自由故》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具