Android LruBitmapPool详情

Android LruBitmapPool详情

1. 初始化

    // Exposed for testing only. 私有构造方法
    LruBitmapPool(int maxSize, LruPoolStrategy strategy, Set<Bitmap.Config> allowedConfigs) {
        this.initialMaxSize = maxSize; // 初始值最大size
        this.maxSize = maxSize; // 最大size
        this.strategy = strategy; // 策略
        this.allowedConfigs = allowedConfigs; // 允许bitmap.config集合
        this.tracker = new NullBitmapTracker(); // 实例化一个空的bitmap追踪器 空实现
    }
	// 传入LRU缓存可接受的最大值
    public LruBitmapPool(int maxSize) {
        this(maxSize, 
        	getDefaultStrategy(),  // 获取默认策略
        	getDefaultAllowedConfigs()); // 获取默认Bitmap.Config
    }
    // 传入LRU缓存可接受的最大值 并设置Bitmap.Config的配置参数信息 默认是ARGB_8888占4个字节
    public LruBitmapPool(int maxSize, Set<Bitmap.Config> allowedConfigs) {
        this(maxSize, getDefaultStrategy(), allowedConfigs);
    }
    // 设置默认的策略
    private static LruPoolStrategy getDefaultStrategy() {
        final LruPoolStrategy strategy; // 设置为LruPoolStrategy 最近最少使用策略
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // >= 19
        	// bitmap size 和 bitmap.config一起作为key保存信息
            strategy = new SizeConfigStrategy(); 
        } else {
        	// 一种重用位图的策略,要求返回的位图的尺寸与这些请求完全匹配。 width height bitmap.config 作为key
            strategy = new AttributeStrategy(); 
        }
        return strategy;
    }

    private static Set<Bitmap.Config> getDefaultAllowedConfigs() {
        Set<Bitmap.Config> configs = new HashSet<Bitmap.Config>();
        configs.addAll(Arrays.asList(Bitmap.Config.values()));
        if (Build.VERSION.SDK_INT >= 19) { // SDK版本大于19 添加一个null结尾
            configs.add(null);
        }
        // 这个方法的作用主要是将集合转为不可更改的集合 里面使用final修饰并返回当前集合
        return Collections.unmodifiableSet(configs); 
    }
Bitmap.Config.values()
    public static enum Config { // 所有数据信息
        ALPHA_8,
        RGB_565,
        ARGB_4444,
        ARGB_8888,
        RGBA_F16,
        HARDWARE;
        private Config() {
        }
    }

2. 使用

使用put向缓存池内添加bitmap数据

    public synchronized boolean put(Bitmap bitmap) {
        if (bitmap == null) { // bitmap不能为null
            throw new NullPointerException("Bitmap must not be null");
        } else if (bitmap.isMutable() && this.strategy.getSize(bitmap) <= this.maxSize && this.allowedConfigs.contains(bitmap.getConfig())) {
        // bitmap.isMutable() 可复用 
        // this.strategy.getSize(bitmap) <= this.maxSize bitmap大小小于等于最大size
        // this.allowedConfigs.contains(bitmap.getConfig()) bitmap.config在集合中有
            int size = this.strategy.getSize(bitmap); // 获取当前bitmap的size大小
            this.strategy.put(bitmap); // 将bitmap保存到策略中
            this.tracker.add(bitmap); // 将bitmap保存到追踪器中 因为使用的是NullBitmapTracker空实现 不做处理
            ++this.puts; // 添加数据 自增
            this.currentSize += size; // 当前使用size
            if (Log.isLoggable("LruBitmapPool", 2)) {
                Log.v("LruBitmapPool", "Put bitmap in pool=" + this.strategy.logBitmap(bitmap));
            }

            this.dump(); // 打印相关log
            this.evict(); // 开始判断当前占用空间大小与总大小的处理
            return true; // 添加成功返回true
        } else {
            if (Log.isLoggable("LruBitmapPool", 2)) {
                Log.v("LruBitmapPool", "Reject bitmap from pool, bitmap: " + this.strategy.logBitmap(bitmap) + ", is mutable: " + bitmap.isMutable() + ", is allowed config: " + this.allowedConfigs.contains(bitmap.getConfig()));
            }

            return false;
        }
    }
this.strategy.put(bitmap); SizeConfigStrategy . put(Bitmap bitmap)
    public void put(Bitmap bitmap) {
        int size = Util.getBitmapByteSize(bitmap); // 获取bitmap的size
        // 将bitmap的size 和 bitmap.config作为参数创建一个key
        SizeConfigStrategy.Key key = this.keyPool.get(size, bitmap.getConfig());
        this.groupedMap.put(key, bitmap); // 将key和对应的bitmap添加到对应的链表map集合中
        // 获取当前bitmap.config类型对应的treeMap集合
        NavigableMap<Integer, Integer> sizes = this.getSizesForConfig(bitmap.getConfig());
        Integer current = (Integer)sizes.get(key.size); // 根据bitmap的大小获取value
        sizes.put(key.size, current == null ? 1 : current + 1); //更新集合内的数据 如果为null? 1 : current+1
    }
    
    static class KeyPool extends BaseKeyPool<SizeConfigStrategy.Key> {
        KeyPool() { }
        public SizeConfigStrategy.Key get(int size, Config config) {
            SizeConfigStrategy.Key result = (SizeConfigStrategy.Key)this.get(); // 获取一个实例对象key
            result.init(size, config); // 对传入的size和config进行初始化 
            return result;
        }
        protected SizeConfigStrategy.Key create() { // 创建一个key实例对象
            return new SizeConfigStrategy.Key(this);
        }
    }
    
    private NavigableMap<Integer, Integer> getSizesForConfig(Config config) {
    	// 获取集合中是否有符合bitmap.config的数据信息
        NavigableMap<Integer, Integer> sizes = (NavigableMap)this.sortedSizes.get(config);
        if (sizes == null) { // 没有相关数据
            sizes = new TreeMap(); // 实例化一个树map
            this.sortedSizes.put(config, sizes); // 将bitmap.config 和 treeMap添加到HashMap集合中
        }
        return (NavigableMap)sizes; // 返回当前集合信息
    }
BaseKeyPool
abstract class BaseKeyPool<T extends Poolable> {
    private static final int MAX_SIZE = 20;
    private final Queue<T> keyPool = Util.createQueue(20);
    BaseKeyPool() { }
    protected T get() {
        T result = (Poolable)this.keyPool.poll(); // 移除并返问队列头部的元素 如果队列为空,则返回null
        if (result == null) { // 如果队列为空 即创建一个实例对象
            result = this.create(); // 调用到KeyPool.create()方法
        }
        return result; // 返回当前
    }
    public void offer(T key) {
        if (this.keyPool.size() < 20) {
            this.keyPool.offer(key); // 添加一个元素并返回true,如果队列已满,则返回false
        }
    }
    protected abstract T create();
}
Queue相关方法
	add            增加一个元索,如果队列已满,则抛出一个IIIegaISlabEepeplian异常

remove 移除并返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常
  element 返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常
  offer 添加一个元素并返回true,如果队列已满,则返回false
  poll 移除并返问队列头部的元素,如果队列为空,则返回null
  peek 返回队列头部的元素,如果队列为空,则返回null
  put 添加一个元素,如果队列满,则阻塞
  take 移除并返回队列头部的元素,如果队列为空,则阻塞

this.groupedMap.put(key, bitmap); 将key和对应的bitmap添加到集合中
GroupedLinkedMap . class
    public void put(K key, V value) { // K Key V Bitmap
    	// 根据key获取到缓存集合中对应的Entry实例类信息
        GroupedLinkedMap.LinkedEntry<K, V> entry = (GroupedLinkedMap.LinkedEntry)this.keyToEntry.get(key);
        if (entry == null) { // 如果集合中没有
            entry = new GroupedLinkedMap.LinkedEntry(key); // 新建一个Entry
            this.makeTail(entry); // 添加到尾部
            this.keyToEntry.put(key, entry); // 在将entry和对应的key添加到keyToEntry集合中
        } else {
            key.offer(); // 添加一个元素并返回true,如果队列已满,则返回false
        }
        entry.add(value); // 将value添加到entry中
    }
    private void makeTail(GroupedLinkedMap.LinkedEntry<K, V> entry) {
        removeEntry(entry); // 将entry从当前链表中进行移除
        entry.prev = this.head.prev; // 将entry的前一个指向head的前一个对象
        entry.next = this.head; // 将entry的下一个指向head
        updateEntry(entry); // 更新entry
    }
    // 双向链表 移除当前的entry对象 从链表是移除
    private static <K, V> void removeEntry(GroupedLinkedMap.LinkedEntry<K, V> entry) {
        entry.prev.next = entry.next; // 将当前entry的前一个指向下一个对象改为指向entry的下一个
        entry.next.prev = entry.prev; // 将当前entry的下一个指向前一个对象改为指向entry的上一个
    }
    private static <K, V> void updateEntry(GroupedLinkedMap.LinkedEntry<K, V> entry) {
        entry.next.prev = entry; // 将entry的下一个的prev指向entry
        entry.prev.next = entry; // 将entry的上一个的next执行entry
    }

下面是操作图示
removeEntry
在这里插入图片描述
makeTail
在这里插入图片描述
updateEntry
在这里插入图片描述

this.dump() 打印相关信息
    private void dump() {
        if (Log.isLoggable("LruBitmapPool", 2)) {
            this.dumpUnchecked();
        }
    }
    private void dumpUnchecked() {
        Log.v("LruBitmapPool", "Hits=" + this.hits + ", misses=" + this.misses + ", puts=" + this.puts + ", evictions=" + this.evictions + ", currentSize=" + this.currentSize + ", maxSize=" + this.maxSize + "\nStrategy=" + this.strategy);
    }
this.evict();
    private void evict() {
        this.trimToSize(this.maxSize);
    }
    private synchronized void trimToSize(int size) { // 传入最大的size
        for(; this.currentSize > size; this.dump()) { // 如果currentSize > size 需要做处理
            Bitmap removed = this.strategy.removeLast(); // 移除最近最少使用的数据
            if (removed == null) { // 如果没有 表示已经没有数据了 将currentSize设置为0 并返回
                if (Log.isLoggable("LruBitmapPool", 5)) {
                    Log.w("LruBitmapPool", "Size mismatch, resetting");
                    this.dumpUnchecked();
                }
                this.currentSize = 0;
                return;
            }
            this.tracker.remove(removed); // 将追踪器中的对象进行移除
            this.currentSize -= this.strategy.getSize(removed); // currentSize进行重新设置
            removed.recycle(); // bitmap对象回收处理
            ++this.evictions; // 操作+1
            if (Log.isLoggable("LruBitmapPool", 3)) {
                Log.d("LruBitmapPool", "Evicting bitmap=" + this.strategy.logBitmap(removed));
            }
        }
    }
this.strategy.removeLast(); SizeConfigStrategy . removeLast()
    public Bitmap removeLast() {
        Bitmap removed = (Bitmap)this.groupedMap.removeLast(); // 获取当前需要移除的bitmap对象
        if (removed != null) {
            int removedSize = Util.getBitmapByteSize(removed); // 获取当前bitmap的size
            this.decrementBitmapOfSize(removedSize, removed.getConfig()); // 减少位图size
        }
        return removed;
    }
    private void decrementBitmapOfSize(Integer size, Config config) {
        NavigableMap<Integer, Integer> sizes = this.getSizesForConfig(config); // 根据config获取到treeMap集合
        Integer current = (Integer)sizes.get(size); // 根据bitmap的size获取当前使用数量
        if (current == 1) { // 是有一次
            sizes.remove(size); // 直接进行remove处理
        } else {
            sizes.put(size, current - 1); // 更新集合 并将使用次数进行-1操作
        }
    }
(Bitmap)this.groupedMap.removeLast(); GroupedLinkedMap . removeLast()
    public V removeLast() {
    	// last.equals(this.head) 数据头与数据头的前一个数据不等 表示有数据 否则表示没有数据直接返回null
        for(GroupedLinkedMap.LinkedEntry last = this.head.prev; !last.equals(this.head); last = last.prev) {
            V removed = last.removeLast(); // 从集合中进行移除并返回移除元素的对象
            if (removed != null) { // 对象不为空
                return removed; // 直接将对象返回
            }
            removeEntry(last); // 直接移除当前entry
            this.keyToEntry.remove(last.key); // keyToEntry中移除掉对应的信息
            ((Poolable)last.key).offer(); // 添加一个元素并返回true,如果队列已满,则返回false
        }
        return null;
    }
((Poolable)last.key).offer(); 调用到 SizeConfigStrategy.Key.offer()
        public void offer() {
            this.pool.offer(this); // 将当前的key对象添加到pool队列中
        }
BaseKeyPool.offer()
    public void offer(T key) {
        if (this.keyPool.size() < 20) { // 缓存池中小于最大数
            this.keyPool.offer(key); // 添加一个元素
        }
    }
GroupedLinkedMap.LinkedEntry.removeLast() V bitmap K SizeConfigStrategy.Key
        public V removeLast() {
            int valueSize = this.size(); // 获取当前values集合的size
            // 如果为0 则返回null 不为0 则移除最后一个元素 并返回当前移除元素的Value对象
            return valueSize > 0 ? this.values.remove(valueSize - 1) : null; 
        }
        public int size() {
            return this.values != null ? this.values.size() : 0; // 集合不为null 返回size 为空 返回0
        }

3.获取

使用中获取对应的bitmap

    public synchronized Bitmap get(int width, int height, Config config) {
    	// 获取到当前目标bitmap 并将当前bitmap从集合中移除
        Bitmap result = this.getDirty(width, height, config);
        if (result != null) { // 如果不为null
            result.eraseColor(0);
        }
        return result; // 返回得到的bitmap
    }

    @TargetApi(12)
    public synchronized Bitmap getDirty(int width, int height, Config config) {
    	// 根据 宽高config 来拿到一个Bitmap对象
        Bitmap result = this.strategy.get(width, height, config != null ? config : DEFAULT_CONFIG);
        if (result == null) { // 如果为空
            if (Log.isLoggable("LruBitmapPool", 3)) {
                Log.d("LruBitmapPool", "Missing bitmap=" + this.strategy.logBitmap(width, height, config));
            }

            ++this.misses; // 自增
        } else {
            ++this.hits; // 自增
            this.currentSize -= this.strategy.getSize(result); // 获取bitmap的size 并减去
            this.tracker.remove(result); // 追踪器集合中移除
            if (VERSION.SDK_INT >= 12) { // SDK 大于等于 12
                result.setHasAlpha(true); // 是否设置Alpha true
            }
        }
        if (Log.isLoggable("LruBitmapPool", 2)) {
            Log.v("LruBitmapPool", "Get bitmap=" + this.strategy.logBitmap(width, height, config));
        }
        this.dump(); // 打印相关信息
        return result;
    }
this.strategy.get(width, height, config != null ? config : DEFAULT_CONFIG);
    public Bitmap get(int width, int height, Config config) {
        int size = Util.getBitmapByteSize(width, height, config); // 根据宽高config类型 获取bitmap的size
        SizeConfigStrategy.Key targetKey = this.keyPool.get(size, config); // 根据size和config获取到key对象
        SizeConfigStrategy.Key bestKey = this.findBestKey(targetKey, size, config); // 获取最好的key
        Bitmap result = (Bitmap)this.groupedMap.get(bestKey); // 根据key获取到GroupMap中对应的bitmap 并移除当前集合
        if (result != null) { // bitmap不为null
        	// 根据config 移除/current-1 sortedSizes分类集合处理
            this.decrementBitmapOfSize(Util.getBitmapByteSize(result), result.getConfig());
            // 对bitmap进行重新配置 config为null 设置默认为ARGB_8888
            result.reconfigure(width, height, result.getConfig() != null ? result.getConfig() : Config.ARGB_8888);
        }
        return result;
    }
    private SizeConfigStrategy.Key findBestKey(SizeConfigStrategy.Key key, int size, Config config) {
        SizeConfigStrategy.Key result = key;
        Config[] arr$ = getInConfigs(config); // 根据对应的config获取到对应的config集合
        int len$ = arr$.length; // 获取config集合的长度

        for(int i$ = 0; i$ < len$; ++i$) {
            Config possibleConfig = arr$[i$];
            // 获取config对应的集合 TreeMap
            NavigableMap<Integer, Integer> sizesForPossibleConfig = this.getSizesForConfig(possibleConfig);
            // 1. 如果有对应相等的size,就返回相等的key对应的size
            // 2. 如果有对应比root小的,就返回比目标size稍微大一些的var值
            // 3. 如果有对应比root大的,并且一直比跟所有的根节点大,返回root。 如果没有比所有根节点大就返回稍微大一点的key对应的size
            Integer possibleSize = (Integer)sizesForPossibleConfig.ceilingKey(size);
            if (possibleSize != null && possibleSize <= size * 8) { // 如果root为null 就返回null && 获取size <= size * 8
                if (possibleSize == size) { // possibleSize == size 好像只有possibleSize=0 size=0 成立
                    if (possibleConfig == null) { // config为null
                        if (config == null) { //  传入config为null
                            break; // 退出循环
                        }
                    } else if (possibleConfig.equals(config)) { // config相等
                        break; // 退出循环
                    }
                }
                this.keyPool.offer(key); // 向缓存池中添加key对象
                result = this.keyPool.get(possibleSize, possibleConfig); // 获取当前size的一个key对象 使用缓存池发方式节省内存
                break; // 退出循环
            }
        }
        return result;
    }
    private static Config[] getInConfigs(Config requested) {
        switch(requested) {
        case ARGB_8888:
            return ARGB_8888_IN_CONFIGS;
        case RGB_565:
            return RGB_565_IN_CONFIGS;
        case ARGB_4444:
            return ARGB_4444_IN_CONFIGS;
        case ALPHA_8:
            return ALPHA_8_IN_CONFIGS;
        default:
            return new Config[]{requested};
        }
    }
(Bitmap)this.groupedMap.get(bestKey);// 根据key获取到对应的bitmap对象
    public V get(K key) {
    	// 获取key对应的链表entry
        GroupedLinkedMap.LinkedEntry<K, V> entry = (GroupedLinkedMap.LinkedEntry)this.keyToEntry.get(key); 
        if (entry == null) { // 如果为空
            entry = new GroupedLinkedMap.LinkedEntry(key); // 实例化一个链表entry对象
            this.keyToEntry.put(key, entry); // 将key和entry添加到集合
        } else {
            key.offer(); // 添加一个元素
        }
        this.makeHead(entry); // entry添加到链表头
        return entry.removeLast(); // 移除操作 并返回移除的bitmap对象
    }
TreeMap.ceilingKey()
    public K ceilingKey(K var1) {
        return keyOrNull(this.getCeilingEntry(var1));
    }
    static <K, V> K keyOrNull(TreeMap.Entry<K, V> var0) {
        return var0 == null ? null : var0.key;
    }
    final TreeMap.Entry<K, V> getCeilingEntry(K var1) {
        TreeMap.Entry var2 = this.root; // 获取到根节点
        while(var2 != null) { // 如果点前节点不为null
            int var3 = this.compare(var1, var2.key); // 将根节点的key 与 传入进来的key进行对比
            if (var3 < 0) { // 表示var1 比 var2.key 小 向左边进行遍历
                if (var2.left == null) { // 左边节点为null
                    return var2; // 直接返回当前var值
                }
                var2 = var2.left; // 继续向左边延伸
            } else { // >= 0 的情况 表示 var1 大于等于 var2.key
                if (var3 <= 0) { // 相等的情况 
                    return var2; // 直接返回当前var
                }
                if (var2.right == null) { // 右节点为null
                    TreeMap.Entry var4 = var2.parent; // 获取到var2的父节点
                    // 进行遍历获取到第一个连续又节点的var值
                    for(TreeMap.Entry var5 = var2; var4 != null && var5 == var4.right; var4 = var4.parent) {
                        var5 = var4;
                    }
                    return var4;
                }
                var2 = var2.right; // 继续向右延伸
            }
        }
        return null; // 返回空
    }

红色的是树节点分发的方向,蓝色是假设当前解析走向。最终是以right结尾。然后查询到绿色位置并返回当前位置var对象。图有点草率……
在这里插入图片描述

result.eraseColor(0);
    public void eraseColor(@ColorInt int c) {
        checkRecycled("Can't erase a recycled bitmap"); // 检查是否被回收 回收则抛出异常
        if (!isMutable()) { // 是否可复用
            throw new IllegalStateException("cannot erase immutable bitmaps");
        }
        nativeErase(mNativePtr, c); // native 擦除处理
    }

4. 总结

  1. 构建LRU缓存池时,有两种策略: AttributeStrategy(width,height,config)作为key;SizeConfigStrategy(size,config)作为key进行存储信息。
  2. 如果未进行设置Bitmap.Config,默认是ARGB_8888一个像素会占用4个byte,会消耗内存较多。如果可以进行设置一下为RGB_565一个像素占用2个byte。内存节约了一半。
  3. 添加bitmap时,会一直判断currentSize是否大于maxSize,只要开始大于,就开始移除掉最近最少使用的bitmap元素,然后将移除掉的元素添加到keyPool中,以供下次put的时候获取资源使用,以此来节省内存空间。
  4. 获取对应的bitmap时,会将之前存放在集合中的数据进行移除操作,包括追踪器和groupedMap集合中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值