Bitmap的缓存结构设计

1. 整体思路设计

采用三级缓存结构:内存-磁盘-网络,缓存使用的是LruCache算法,最近最少使用缓存算法

  1. 内存缓存使用API自带实现的LruCache来满足
  2. 磁盘缓存使用官方推荐的DiskLruCache来满足
  3. 内存资源比较珍贵,在LruCache的基础上,增加了复用池及回收队列来提高效率
Bitmap缓存结构.png

2. 分块解析

A. 获取结构

这部分结构比较简单,依次取出,为空时进行资源获取,同时对获取的资源进行大小优化、内存复用

    /**
     * 返回压缩图片
     *
     * @param context
     * @param filePath
     * @param maxW
     * @param maxH
     * @param hasAlpha
     * @return
     */
    public static Bitmap resizeBitmap(Context context, String filePath, int maxW, int maxH, boolean hasAlpha, Bitmap reusable) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        // 设置为true后,再去解析,就只解析 out 参数
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);
        int w = options.outWidth;
        int h = options.outHeight;
        options.inSampleSize = calcuteInSampleSize(w, h, maxW, maxH);
        if (!hasAlpha) {
            options.inPreferredConfig = Bitmap.Config.RGB_565;
        }
        options.inJustDecodeBounds = false;
        // 复用, inMutable 为true 表示易变
        options.inMutable = true;
        options.inBitmap = reusable;
        return BitmapFactory.decodeFile(filePath, options);
    }

大小优化:按需解析(宽高、是否需要透明度)

内存复用:使用可复用的内存(inBitmap),并将自身设置可变(inMutable)

B. 缓存结构拆分

磁盘缓存并不需要额外的优化,所以此处最主要是对内存资源的利用,这部分的利用分两方面:

  • 内存复用:复用池、Bitmap的复用开关

    内存资源的开辟消耗是比较大的,对于已经申请的内存资源直接复用起来是比较好的提升手段( 优化释放旧Bitmap内存以及重新申请Bitmap内存导致的性能损耗 );通过复用池,在缓存空间被用完时,对将移除释放的缓存进行复用池复用,在下一次新加入缓存时直接利用这块内存空间

  • 内存回收效率提升:复用池、弱引用、阻塞队列

    GC在工作时,会扫描到复用池中不被使用的对象,并标记;此时,弱引用会将被标记的对象放入配置的阻塞队列,阻塞队列对该对象进行主动回收(效率高于GC)

3. 功能点解析

关联:缓存+复用池+弱引用+回收队列

//内存缓存 大小一般默认取当前应用内存的1/8
lruCache = new LruCache(SystemUtils.getAppMemoryForByte(context) / 8) {
    @Override
    protected int sizeOf(String key, Bitmap value) {
        return BitmapUtils.getBitmapSize(value);
    }
    @Override
    protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
        if (oldValue.isMutable()) {
          
            //将移除对象与阻塞队列配置为弱引用对象放入复用池(WeakReference在oldValue标记回收时会被添加到 设置的引用队列中)
            reusablePool.add(new WeakReference(oldValue, getReferenceQueue()));
        }else {
            oldValue.recycle();
        }

    }
};

复用池的获取:

/**
 * 复用 指使用 重新使用 之前bitmap占用的内存块
 * 3.0 之前不能复用
 * 3.0-4.4 宽高一样,inSampleSize = 1 自己复用自己
 * 4.4 只要小于等于就行了  小的可以使用大的
 *
 * @param w
 * @param h
 * @param inSampleSize
 * @return
 */
public Bitmap getReusable(int w, int h, int inSampleSize) {
    //3.0 以下没有复用功能
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        return null;
    }
    Bitmap reusable = null;
    Iterator> iterator = reusablePool.iterator();
    while (iterator.hasNext()) {
        Bitmap bitmap = iterator.next().get();
        if (bitmap != null) {
            if (checkInBitmap(bitmap, w, h, inSampleSize)) {
                reusable = bitmap;
                iterator.remove();
                break;
            }
        } else {
            iterator.remove();
        }
    }
    return reusable;
}
/**
 * 校验bitmap 是否满足复用条件
 *
 * @param bitmap
 * @param w
 * @param h
 * @param inSampleSize
 * @return
 */
private boolean checkInBitmap(Bitmap bitmap, int w, int h, int inSampleSize) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
        return bitmap.getWidth() == w && bitmap.getHeight() == h && inSampleSize == 1;
    }

    if (inSampleSize > 1) {
        w /= inSampleSize;
        h /= inSampleSize;
    }
    int byteCount = w * h * getBytesPerPixel(bitmap.getConfig());
    // 图片内存 系统分配内存
    return byteCount <= bitmap.getAllocationByteCount();
}

阻塞队列:主动回收

private ReferenceQueue getReferenceQueue() {
    if (referenceQueue == null) {
        referenceQueue = new ReferenceQueue<>();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!shutDown) {
                    try {
                        //remove带阻塞功能,
                        Reference remove = referenceQueue.remove();
                        Bitmap bitmap = remove.get();
                        if (bitmap != null && !bitmap.isRecycled()) {
                            bitmap.recycle();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
    return referenceQueue;
}

4. 功能封装

Bitmap:缓存管理

你可能感兴趣的:(Bitmap的缓存结构设计)