十二、Bitmap的加載和Cache

Bitmap的加載和Cache,目前比較常用的緩存策略是LruCach和DisLruCache,其中LruCache常被用作內存緩存,而DisLruCache常被用做存儲緩存。
Lru是Least Recently Used即最近最少使用算法,這種算法的核心思想是,當緩存快滿時,會淘汰近期最少使用的緩存目標。

1.Bitmap的高效加載

加載圖片,BitmapFactory類提供了四類方法:
decodeFile,decodeResource,decodeStream,decodeByteArray,分別用於從文件系統,資源,輸入流,以及字節數組中加載出一個Bitmap對象,其中decodeFile和decodeResource又間接調用了decodeStream方法。

如何高效的加載圖片呢?
核心思想就是採用BitmapFactory.Options來加載所需尺寸的圖片。通過BitmapFactory.Options來縮放圖片,主要是用到了它的inSampleSize參數,即採樣率。

當inSampleSize爲1時,採樣後的圖片大小爲圖片的原始大小,當inSampleSize大於1時,比如爲2,那麼採樣後的圖片其寬高均爲原圖大小的1/2,而像素爲原圖的1/4,其佔用的內存大小也爲原圖的1/4.

如一張1024*1024像素的圖,採用ARGB8888格式存儲,那麼它佔用的內存爲1024*1024*4,即4M,如果inSampleSize爲2,那麼採樣後佔用的內存爲512*512*4,即1M。inSampleSize的縮放比例爲(1/inSampleSize)2。

實際情況:
如果ImageView的大小爲100*100像素,而圖片的原始大小爲200*200,那麼只需要將採樣率inSampleSize設置爲2即可。如果圖片爲200*300呢,採樣率還是設置爲2,這樣縮放後的圖片大小爲100*150,仍然是適合的。如果採樣率爲3,那麼縮放後的圖片大小就會小於ImageView所期望的大小,這樣圖片就會被拉伸導致模糊。

通過採樣率可以高效的加載圖片,如何獲取採樣率呢?
(1)將BitmapFactory.Options的inJustDecodeBounds參數設置爲true並加載圖片;
(2)從BitmapFactory.Options中取出圖片的原始寬高信息,它們對應於outWidth和outHeight參數;
(3)根據採樣率的規則並結合目標View的所需大小計算採樣率inSampleSize;
(4);將BitmapFactory.Options的inJustDecodeBounds參數設置爲false並重新加載圖片;

注意:inJustDecodeBounds這個參數爲true時,BitmapFactory只會解析圖片的原始寬高信息,並不會真正地加載圖片,所以這個操作是輕量級的。
另外需要注意的是,這個時候BitmapFactory獲取的圖片寬高信息和圖片的位置以及程序運行的設備有關,比如同一張圖片放在不同的Drawable目錄下或者程序運行在不同屏幕密度的設備上,都可能導致BitmapFactory獲取到不同的結果。

public class ImageResizer {
    private static final String TAG = "ImageResizer";
    public ImageResizer() {
    }
    public Bitmap decodeSampledBitmapFromResource(Resources res,
            int resId, int reqWidth, int reqHeight) {
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth,
                reqHeight);
        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }
    public Bitmap decodeSampledBitmapFromFileDescriptor(FileDescriptor fd, int reqWidth, int reqHeight) {
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFileDescriptor(fd, null, options);
        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth,
                reqHeight);
        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFileDescriptor(fd, null, options);
    }
    public int calculateInSampleSize(BitmapFactory.Options options,
            int reqWidth, int reqHeight) {
        if (reqWidth == 0 || reqHeight == 0) {
            return 1;
        }
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        Log.d(TAG, "origin, w= " + width + " h=" + height);
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            // Calculate the largest inSampleSize value that is a power of 2 and
            // keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) >= reqHeight
                    && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }
        Log.d(TAG, "sampleSize:" + inSampleSize);
        return inSampleSize;
    }
}

2.Android中的緩存策略

2.1.LruCache

LruCache是一個泛型類,它內部採用一個LinkedHashMap以強引用的方式存儲外界緩存對象,其提供了get和put方法來完成緩存的獲取和添加操作,當緩存滿時,LruCache會移除較早使用的緩存對象,然後再添加新的緩存對象。
強引用:直接的對象引用;
軟引用:當一個對象只有軟引用存在時,系統內存不足時此對象會被gc回收;
弱引用:當一個對象只有軟引用存在時,此對象會隨時被gc回收;
另外LruCache是線程安全的。
經典的初始化代碼:

        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };

sizeof方法的作用是計算緩存對象的大小,這裏大小的單位需要和總容量的單位一致。上面總容量爲當前進程可用內存的1/8,單位爲KB,sizeof方法則完成了Bitmap對象的大小計算。除以1024也是把單位轉換爲KB。

2.2.DiskLruCache

2.3.ImageLoader的實現

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