Glide 中 Bitmap 的图片复用 (3.7.0为例 6)

Bitmap 在app应用中是个占内存的大块头,google 公司也一直对它做优化,比如说存储机制,Android 2.3.3及以前版本,Bitmap 的像素点数据是保存在 Native Memory(C/C++),Bitmap对象则是保存在 Dalvik heap(Java); 从Android 3.0开始,像素点数据与Bitmap对象一起存储在 Dalvik heap中。从 Android 8.0开始,Bitmap 的像素点数据又保存在Native Memory中。

Android 3.0时,在 BitmapFactory.Options 中新增 inBitmap 字段,通过这个参数,可以复用之前的 Bitmap,前提是这两个图片占用内存大小是一样的,这样就避免了再创建一个Bitmap,实现了复用。 Android 4.4,inBitmap功能增强了,需要用的图片比着要复用的图片小即可,比如第一次创建了图片A,现在要创建图片B,我们可以复用A的前提是 B <= A。 以上的前提是被复用的图片 BitmapFactory.Options 中的 mIsMutable 字段需要设置为true。简单的例子,这里注意,图片占内存比较 b <= a(还需要版本判断)
 

BitmapFactory.Options aOption = new BitmapFactory.Options();
aOption.inMutable = true; // 设置 inMutable
Bitmap aBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.a, aOption);

BitmapFactory.Options bOption = new BitmapFactory.Options();
bOption.inBitmap = aBitmap; // 设置 inBitmap 被复用的 Bitmap
Bitmap bBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.b, bOption);


Glide 中的 Bitmap 缓存池复用功能设计的很好,甚至有人把它里面这部分代码给抽取出来单独使用。如果项目中引入了 Glide,可以直接通过 Glide.get(context).getBitmapPool() 来获取 BitmapPool pool 对象,然后 Bitmap bitmap = pool.get(width, height, Bitmap.Config.ARGB_8888) 来获取复用的 Bitmap。Glide 中关于这一块设计的也比较巧妙,GlideBuilder 中的 createGlide() 方法

   private BitmapPool bitmapPool;
   Glide createGlide() {
        ...
        if (bitmapPool == null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                int size = calculator.getBitmapPoolSize();
                bitmapPool = new LruBitmapPool(size);
            } else {
                bitmapPool = new BitmapPoolAdapter();
            }
        }
        ...
        return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
    }

在这个方法中创建了 BitmapPool 对象,然后传递给了 Glide,Glide 是个单例模式,所以我们可以全局使用。这里看看 bitmapPool 是怎么创建的,它有个版本判断,大于等于 11 则是 LruBitmapPool 类型,否则是 BitmapPoolAdapter 类型,BitmapPoolAdapter 里面都是空方法,既不能存,也不能取,这么设计是因为低于11的系统版本无Bitmap复用的功能,所以这里有个分割,做了版本兼容,我们看看 LruBitmapPool 的代码

    @Override
    public synchronized boolean put(Bitmap bitmap) {
        ...
        strategy.put(bitmap);
        ...
        return true;
    }

    @Override
    public synchronized Bitmap get(int width, int height, Bitmap.Config config) {
        Bitmap result = getDirty(width, height, config);
        ...
        return result;
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
    @Override
    public synchronized Bitmap getDirty(int width, int height, Bitmap.Config config) {
        final Bitmap result = strategy.get(width, height, config != null ? config : DEFAULT_CONFIG);
        ...
        return result;
    }


看过 put() 和 get() 方法,发现里面最终还是用 strategy 来做具体的工作,那么看看 strategy 是什么,它是 LruPoolStrategy,又是个接口,熟悉的配方,看看它是怎么创建的

    private static LruPoolStrategy getDefaultStrategy() {
        final LruPoolStrategy strategy;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            strategy = new SizeConfigStrategy();
        } else {
            strategy = new AttributeStrategy();
        }
        return strategy;
    }

这里又是版本判断,本次判断的依据是版本19,看到这,我们也能反应过来是怎么回事了,因为Bitmap复用,19之后和之前的要求不一样,而 SizeConfigStrategy 和 AttributeStrategy 里面的代码则体现出这一逻辑。看下 AttributeStrategy 的代码

    public void put(Bitmap bitmap) {
        final Key key = keyPool.get(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());

        groupedMap.put(key, bitmap);
    }

    @Override
    public Bitmap get(int width, int height, Bitmap.Config config) {
        final Key key = keyPool.get(width, height, config);

        return groupedMap.get(key);
    }

这里面牵涉到两个类,KeyPool 和 GroupedLinkedMap,明显 KeyPool 是来缓存 key 的,而 GroupedLinkedMap 则主要是存放 key 及对应的 Bitmap,先看 KeyPool,它继承了 BaseKeyPool,里面是个上限为20的队列,如果需要对象,先从队列中取,如果队列为空,则new一个对象;往里面存储时,则先判断队列是否达到上限,如果没达到上限,则放入队列中。GroupedLinkedMap 的代码也比较有意思,它其实类似 new HashMap<key,LinkedList>,  LinkedEntry 本身类似 LinkedList中的
Node 节点,可以持有前后的引用,成一个串,同时 LinkedEntry 中有个 List 的属性,可以把相同key对应的值都放到一个集合中;head 则作为一个节点,当有新的 LinkedEntry 创建时,则会把新的LinkedEntry放到头部,这样就形成了类似一个LruCache的功能,head 的作用也就在此,它就是一个头部标志,离他越远,说明使用频率就越低,所以回收对象时就有优先级了。 AttributeStrategy 中数据的宽和高的数据要求比较严,因为android版本11-18要求如此。 SizeConfigStrategy则复杂点,大家可以自己读一下。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值