Glide 源码分析之 BitmapPool

bitmap的缓存

在build Glide 时 会初始化根据版本不同会选择一个bitmap的缓存策略 LruBitmapPool

    Glide createGlide() {
        if (sourceService == null) {
            final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
            sourceService = new FifoPriorityThreadPoolExecutor(cores);
        }
        if (diskCacheService == null) {
            diskCacheService = new FifoPriorityThreadPoolExecutor(1);
        }

        MemorySizeCalculator calculator = new MemorySizeCalculator(context);
        if (bitmapPool == null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                int size = calculator.getBitmapPoolSize();
                bitmapPool = new LruBitmapPool(size);//**注意这里** 如果大于11 就会创建一个默认的Lru算法的bitmap缓存池
            } else {
                bitmapPool = new BitmapPoolAdapter();
            }
        }

        if (memoryCache == null) {
            memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
        }

        if (diskCacheFactory == null) {
            diskCacheFactory = new InternalCacheDiskCacheFactory(context);
        }

        if (engine == null) {
            engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
        }

        if (decodeFormat == null) {
            decodeFormat = DecodeFormat.DEFAULT;
        }

        return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
    }

LruBitmapPool: 负责控制缓存
LruPoolStrategy : 他的实现类 , 负责真正的缓存bitmap

默认每次在put的时候 , 都会累计内存用量 , 判断如果当前使用量大于允许的最大内存就会移除链表的表尾, 最新的默认会移动到表头;这就是最近最少使用算法;

    //可以设置缓存的大小maxSize ,  有2个默认的策略实现,第一个真正缓存功能的实现类 , LruBitmapPool 只是转发类 , 真正实现缓存是他 LruPoolStrategy
    第二个允许bitmap的类型
    /**
     * Constructor for LruBitmapPool.
     *
     * @param maxSize The initial maximum size of the pool in bytes.
     */
    public LruBitmapPool(int maxSize) { 
        this(maxSize, getDefaultStrategy(), getDefaultAllowedConfigs());
    }
    
    // 使用 SizeConfigStrategy 策略 
    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;
    }   
    
    //Bitmap.Config 是个枚举, 把默认bitmap类型都放到容器里
    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) {
            configs.add(null);
        }
        return Collections.unmodifiableSet(configs);
    }   
    
    public static enum Config {
        ALPHA_8,
        ARGB_4444,
        ARGB_8888,
        HARDWARE,
        RGBA_F16,
        RGB_565;

       private Config() {
       }
    }

来分析下 大于19版本 SizeConfigStrategy 他的实现思路;
以put 第二行 , 以bitmap的占用大小 和 他的bitmap像素类型 ,让一个key对象来包裹 , 封装成GroupedLinkedMap的key了, 依靠 自定义的 linkedHashMap , 把匹配的取出来并设置成表头;

GroupedLinkedMap 类:

   // Make the entry the most recently used item.
    private void makeHead(LinkedEntry<K, V> entry) {
        removeEntry(entry);
        entry.prev = head;
        entry.next = head.next;
        updateEntry(entry);
    }
    
    private final KeyPool keyPool = new KeyPool();

    private final GroupedLinkedMap<Key, Bitmap> groupedMap = new GroupedLinkedMap<Key, Bitmap>();
    
    @Override
    public void put(Bitmap bitmap) {
        int size = Util.getBitmapByteSize(bitmap);//bitmap 占用内存大小
        Key key = keyPool.get(size, bitmap.getConfig());

        groupedMap.put(key, bitmap);

        NavigableMap<Integer, Integer> sizes = getSizesForConfig(bitmap.getConfig());
        Integer current = sizes.get(key.size);
        sizes.put(key.size, current == null ? 1 : current + 1);
    }

//keyPool 的 get 方法 , 默认先去队列里找 , 如果不为空就取出来 ,然后从新赋值新的属性 , 以复用对象 , 真是一点内存(多创建一个对象)都不浪费;
    public Key get(int size, Bitmap.Config config) {
            Key result = get();
            result.init(size, config);
            return result;
    }
    
    //队列为空才创建新的
    protected T get() {
        T result = keyPool.poll();
        if (result == null) {
            result = create();
        }
        return result;
    }
    

来看取bitmap , 计算出最匹配的key , 拿出bitmap , 在控制类 减去当前的size占用


    @Override
    public Bitmap get(int width, int height, Bitmap.Config config) {
        int size = Util.getBitmapByteSize(width, height, config);
        Key targetKey = keyPool.get(size, config);
        Key bestKey = findBestKey(targetKey, size, config);

        Bitmap result = groupedMap.get(bestKey);
        if (result != null) {
            // Decrement must be called before reconfigure.
            decrementBitmapOfSize(Util.getBitmapByteSize(result), result.getConfig());
            result.reconfigure(width, height,
                    result.getConfig() != null ? result.getConfig() : Bitmap.Config.ARGB_8888);
        }
        return result;
    }

    private Key findBestKey(Key key, int size, Bitmap.Config config) {
        Key result = key;
        for (Bitmap.Config possibleConfig : getInConfigs(config)) {
            NavigableMap<Integer, Integer> sizesForPossibleConfig = getSizesForConfig(possibleConfig);
            Integer possibleSize = sizesForPossibleConfig.ceilingKey(size);
            if (possibleSize != null && possibleSize <= size * MAX_SIZE_MULTIPLE) {
                if (possibleSize != size
                        || (possibleConfig == null ? config != null : !possibleConfig.equals(config))) {
                    keyPool.offer(key);
                    result = keyPool.get(possibleSize, possibleConfig);
                }
                break;
            }
        }
        return result;
    }


推荐阅读更多精彩内容