Bitmap的緩存結構設計

1. 整體思路設計

採用三級緩存結構:內存-磁盤-網絡,緩存使用的是LruCache算法,最近最少使用緩存算法

  1. 內存緩存使用API自帶實現的LruCache來滿足
  2. 磁盤緩存使用官方推薦的DiskLruCache來滿足
  3. 內存資源比較珍貴,在LruCache的基礎上,增加了複用池及回收隊列來提高效率

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<String, Bitmap>(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<Bitmap>(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<WeakReference<Bitmap>> 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<Bitmap> getReferenceQueue() {
    if (referenceQueue == null) {
        referenceQueue = new ReferenceQueue<>();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!shutDown) {
                    try {
                        //remove帶阻塞功能,
                        Reference<? extends Bitmap> remove = referenceQueue.remove();
                        Bitmap bitmap = remove.get();
                        if (bitmap != null && !bitmap.isRecycled()) {
                            bitmap.recycle();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
    return referenceQueue;
}

4. 功能封裝

Bitmap:緩存管理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章