BitmapPool 了解吗?Glide 是如何实现 Bitmap 复用的?


highlight: agate

这个问题实际上是我前几天面试时遇到的一个问题,虽然我之前分析过 Glide 的源码,但是老实说,如果不是面试遇到这类问题,我根本不会留意 Glide 的 Bitmap 复用这块…不管怎么说,遇到了这个问题,我们就来看下 Glide 是如何实现 Bitmap 复用的吧~

1、“池化”以及对象复用

其实,说起“池化”以及对象复用,在 Android 中例子还是有这么几个的。典型的比如 Handler 中的 Message. 当我们使用 Message 的 obtain 获取消息的受,实际上是从 Message 池中获取的。Handler 中的 Message 是通过链表维护的数据结构,以此来构成一个 “Message 池”。这个池的最大数量由 MAX_POOL_SIZE 这个参数指定,即为 50.

那么,“池化”以及对象复用有什么好处呢?

这是因为对于 Message 这类频繁使用的对象,如果每次使用的时候直接创建一个对象,那么可能会因频繁创建和销毁导致虚拟机 GC,从而造成页面卡顿现象,尤其是在低端设备上面。“池化”之后每次从池子中获取已经创建的对象进行复用,从而避免了虚拟机频繁 GC.

对于 Bitmap 这类对象和图片相关、占用内存较大的对象,如果频繁创建和销毁,对虚拟机的影响可能比 Message 要大得多,因此 Bitmap 复用显得非常重要。

2、从 Bitmap 的回收说起

先看下 Bitmap 是如何进行回收的吧。

根据官方的建议,在 Android 2.3 及以下的版本中建议使用 recycle() 回收内存,防止 OOM. 但是,使用这个方法的前提是需要确保这个位图不再被使用,否则回收之后再使用将会导致运行时错误。所以,官方的建议是通过引用计数的方式统计位图的引用,只有当位图不再被引用的时候再真正调用该方法进行回收。

官方文档参考:https://developer.android.com/topic/performance/graphics/manage-memory

在 Android 3.0 上面引入了 BitmapFactory.Options.inBitmap 字段。如果设置了此选项,那么采用 Options 对象的解码方法会在加载内容时尝试重复使用现有位图。这样可以复用现有的 Bitmap,减少对象创建,从而减少发生 GC 的概率。不过,inBitmap 的使用方式存在某些限制。特别是在 Android 4.4(API 级别 19)之前,系统仅支持大小相同的位图。在 Android 4.4 之后的版本,只要内存大小不小于需求的 Bitmap 都可以复用。

所以,当我们需要在 Android 中使用 Bitmap 的时候,应该考虑进行 Bitmap 复用以提升应用性能。但是,这些复杂的逻辑要如何封装呢?官方的建议是使用比较成熟的图片加载框架,比如 Glide. 所以,接下来我们来分析下 Glide 是如何实现 Bitmap 复用的。

3、Glide 的 BitmapPool

我们直接从 Glide 的 BitmapPool 开始分析。BitmapPool 是一个接口,定义如下:

public interface BitmapPool {
   
  long getMaxSize();
  void setSizeMultiplier(float sizeMultiplier);
  // 往 pool 中插入 bitmap 以备复用
  void put(Bitmap bitmap);
  // 从 pool 中获取 bitmap 以复用
  @NonNull Bitmap get(int width, int height, Bitmap.Config config);
  @NonNull Bitmap getDirty(int width, int height, Bitmap.Config config);
  void clearMemory();
  void trimMemory(int level);
}

BitmapPool 通过定义一个 Pool 来让用户复用 Bitmap 对象。在 Glide 中,BitmapPool 有一个默认的实现 LruBitmapPool. 顾名思义,也是基于 LRU 的理念设计的。

前面我们提到过 inBitmap 以 Android 4.4 为分水岭,之前和之后的版本在使用上存在版本差异,那么 BitmapPool 是如何处理这个差异的呢?答案是策略模式。Glide 定义了 LruPoolStrategy 接口,该接口内部定义了增删相关操作。真实的 Bitmap 数据根据尺寸和颜色等映射关系存储到 LruPoolStrategy 中。BitmapPool 的 get 和 put 也是通过 LruPoolStrategy 的 get 和 put 完成的。

interface LruPoolStrategy {
   
  void put(Bitmap bitmap);
  @Nullable Bitmap get(int width, int height, Bitmap.Config config);
  @Nullable Bitmap removeLast();
  String logBitmap(Bitmap bitmap);
  String logBitmap(<
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值