Bitmap的載入與快取策略

sun_xin發表於2017-12-14

Bitmap的載入和Cache

Bitmap的高效載入

使用BitmapFactory載入一張圖片的方式

  • decodeFile 從檔案
  • decodeResource 從資源
  • decodeStream 從輸入流
  • decodeByteArray 從位元組陣列 核心思想

採用BitmapFactory.Options按照一定的取樣率(inSampleSize)來載入縮小後的圖片,這樣就會降低記憶體的佔用避免OOM,提高了Bitmap載入時的效能 inSampleSize = 1 ,那麼取樣後的圖片是原始圖片的大小。inSampleSize大於1等於2時,取樣後的圖片的寬高均為原圖的二分之一,畫素數為原圖的四分之一

//獲取取樣率
public static Bitmap decodeFile(String path) {
    int finalWidth = 800;//規定寬度如果為800
    //先獲取寬度
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;//圖片不載入到記憶體
    BitmapFactory.decodeFile(path,options);
    //取出圖片的原始寬度
    int outWidth = options.outWidth;
    int inSampleSize = 1;
    if (outWidth>finalWidth){
        inSampleSize = outWidth/finalWidth;
    }
    //設定回去
    options.inSampleSize = inSampleSize;
    options.inJustDecodeBounds = false;


    return BitmapFactory.decodeFile(path,options);
}
複製程式碼

Android中的快取策略

避免過多的流量消耗需要進行快取,當程式第一次從網路載入圖片後,將其快取到儲存裝置上,下次使用的時候就不必再從網路拉取,為了提高使用者體驗,往往還會降圖片再在記憶體中快取一份,這樣當應用打算從網路請求一張圖片的時候,會先從記憶體中獲取,記憶體沒有就從儲存裝置獲取,儲存裝置沒有就在從網路上拉取。

快取策略LRU(Least Recently Userd)演算法: 最近最少使用演算法,當快取滿時,會優先淘汰那些近期最少使用的快取物件。 LruCache用來實現記憶體快取,LruDiskCache用來實現儲存裝置快取。二者可以完美結合。

LruCache

LruCache: 是一個泛型類,是執行緒安全的。內部採用LinkedHashMap以強引用的方式儲存外界的快取物件,提供了get和put方法來完成快取的獲取和新增操作,當快取滿時,LruCache會移除較早使用的快取物件,然後再新增新的快取物件。

  • 強引用: 直接的物件引用;
  • 軟引用: 當一個物件只有軟引用存在的時候,系統記憶體不足的時候,物件會被回收;
  • 弱引用: 當一個物件只有弱引用存在的時候,物件隨時可能被系統回收。
public class LruCache<K, V> {
    private final LinkedHashMap<K, V> map;

複製程式碼

// LruCache 記憶體快取 初始化操作
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;//總容量的大小為當前最大記憶體的八分之一

mLruCache = new LruCache<String, Bitmap>(cacheSize) {
    //計算快取物件的大小
    @Override
    protected int sizeOf(String key, Bitmap bitmap) {
        //注意單位的轉換  bytes --> kb
        return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
    }

    //移除舊快取時呼叫
    @Override
    protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
        super.entryRemoved(evicted, key, oldValue, newValue);
        // TODO: 2017/9/1 資源回收的工作
    }
};

複製程式碼

DiskLruCache

DiskLruCache:磁碟快取,通過將快取物件寫入檔案系統從而實現快取效果

  • DiskLruCache的建立
private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;//磁碟快取50M大小

File diskCacheDir = getExternalCacheDir();
if (!diskCacheDir.exists()){
    diskCacheDir.mkdirs();
}
/**
 * Opens the cache in {@code directory}, creating a cache if none exists
 * there.
 *
 * @param directory a writable directory 快取目錄
 * @param appVersion 應用版本號
 * @param valueCount the number of values per cache entry. Must be positive.
 * @param maxSize the maximum number of bytes this cache should use to store
 * @throws IOException if reading or writing the cache directory fails
 */
DiskLruCache diskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);

複製程式碼
  • DiskLruCache的快取新增 **DiskLruCache.Editor:**表示一個快取物件的編輯物件。最後記得commit() **注意:**因為圖片的url可能含有特殊字元,所以一般採用urlmd5值做為key
String imgUrl = "";
String key = MD5Util.getMd5Value(imgUrl);
DiskLruCache.Editor editor = diskLruCache.edit(key);
if (editor != null) {
    editor.getString(0);
}
editor.commit();//提交
editor.abort();//回退
diskLruCache.flush();//重新整理

複製程式碼
  • DiskLruCache的快取查詢

通過DiskLruCache.get獲取一個SnapShot物件,再使用Snapshot物件即可獲得快取的檔案輸入流。

String key = MD5Util.getMd5Value(imgUrl);
Bitmap bitmap = null;
DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
if (snapshot != null) {
    FileInputStream fileInputStream = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
    //從檔案流中獲取檔案描述符
    FileDescriptor fileDescriptor = fileInputStream.getFD();
    bitmap = decodeSampledBitmapFromFileDescriptor(fileDescriptor,reqWidth,reqHeight);
    if (bitmap != null) {
        addBitmapToMemoryCache(key,bitmap);
    }
}

複製程式碼

FileDescriptor:檔案描述符

直接使用BitmapFactory.OptionsFileInputStream進行縮放會出現問題,因為FileInputStream是一種有序的檔案流,兩次decodeStream呼叫會影響檔案流的位置屬性,導致第二次decodeStream時得到的是null。為了解決這個問題,可以通過檔案流來得到它所對應的檔案描述符,然後通過BitmapFactory.decodeFileDescriptor來載入一張縮放後的圖片。

  • DiskLruCache.delete
  • DiskLruCache.remove

自定義ImageLoader的實現與圖片牆效果

相關文章