内容简介: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); } }
画个数据流图看得更直观一些
注: resource release时机解释:每个资源有一个自己的使用计数,每当使用的时候会调用acquire方法给计数加1,每次job完成后会计数减1,当计数为零时进行release
Object Pool的使用
我们知道如果较短时间内频繁的新建对象是会造成内存的抖动,可能造成界面的卡顿。
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。
其他Tips
-
不同的BitMapConfig不能复用,所以需要用不同的Pool来缓存bitmap。这里也就是Glide里的
com/bumptech/glide/load/engine/bitmap_recycle/SizeConfigStrategy.java 存在的意义
总结
- 从Glide里我们可以学习到如何用LruCache建立多级缓存的逻辑。
- 如何使用inBitmap对bitmap进行缓存
- LinkedHashMap是如何实现LRU管理的(双向循环链表)
## 参考
Glide v4 Caching
Re-using Bitmaps
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- WWDC19 看点
- 腾讯看点视频推荐索引构建方案
- WWDC2017看点分析:那些内容值得期待?
- Glide源码看点-图片pre scale
- 2017年IaaS云计算市场10大看点
- Glide看点(二)巧用Fragment管理生命周期
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Markdown 在线编辑器
Markdown 在线编辑器
HEX HSV 转换工具
HEX HSV 互换工具