Bitmap的加載與緩存策略

Bitmap的加載和Cache

Bitmap的高效加載

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

  • decodeFile 從文件
  • decodeResource 從資源
  • decodeStream 從輸入流
  • decodeByteArray 從字節(jié)數(shù)組
    核心思想

采用BitmapFactory.Options按照一定的采樣率(inSampleSize)來加載縮小后的圖片,這樣就會(huì)降低內(nèi)存的占用避免OOM,提高了Bitmap加載時(shí)的性能
inSampleSize = 1 ,那么采樣后的圖片是原始圖片的大小。inSampleSize大于1等于2時(shí),采樣后的圖片的寬高均為原圖的二分之一,像素?cái)?shù)為原圖的四分之一

//獲取采樣率
public static Bitmap decodeFile(String path) {
    int finalWidth = 800;//規(guī)定寬度如果為800
    //先獲取寬度
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;//圖片不加載到內(nèi)存
    BitmapFactory.decodeFile(path,options);
    //取出圖片的原始寬度
    int outWidth = options.outWidth;
    int inSampleSize = 1;
    if (outWidth>finalWidth){
        inSampleSize = outWidth/finalWidth;
    }
    //設(shè)置回去
    options.inSampleSize = inSampleSize;
    options.inJustDecodeBounds = false;


    return BitmapFactory.decodeFile(path,options);
}

Android中的緩存策略

避免過多的流量消耗需要進(jìn)行緩存,當(dāng)程序第一次從網(wǎng)絡(luò)加載圖片后,將其緩存到存儲(chǔ)設(shè)備上,下次使用的時(shí)候就不必再從網(wǎng)絡(luò)拉取,為了提高用戶體驗(yàn),往往還會(huì)降圖片再在內(nèi)存中緩存一份,這樣當(dāng)應(yīng)用打算從網(wǎng)絡(luò)請(qǐng)求一張圖片的時(shí)候,會(huì)先從內(nèi)存中獲取,內(nèi)存沒有就從存儲(chǔ)設(shè)備獲取,存儲(chǔ)設(shè)備沒有就在從網(wǎng)絡(luò)上拉取。

緩存策略LRU(Least Recently Userd)算法: 最近最少使用算法,當(dāng)緩存滿時(shí),會(huì)優(yōu)先淘汰那些近期最少使用的緩存對(duì)象。
LruCache用來實(shí)現(xiàn)內(nèi)存緩存,LruDiskCache用來實(shí)現(xiàn)存儲(chǔ)設(shè)備緩存。二者可以完美結(jié)合。

LruCache

LruCache: 是一個(gè)泛型類,是線程安全的。內(nèi)部采用LinkedHashMap以強(qiáng)引用的方式存儲(chǔ)外界的緩存對(duì)象,提供了get和put方法來完成緩存的獲取和添加操作,當(dāng)緩存滿時(shí),LruCache會(huì)移除較早使用的緩存對(duì)象,然后再添加新的緩存對(duì)象。

  • 強(qiáng)引用: 直接的對(duì)象引用;
  • 軟引用: 當(dāng)一個(gè)對(duì)象只有軟引用存在的時(shí)候,系統(tǒng)內(nèi)存不足的時(shí)候,對(duì)象會(huì)被回收;
  • 弱引用: 當(dāng)一個(gè)對(duì)象只有弱引用存在的時(shí)候,對(duì)象隨時(shí)可能被系統(tǒng)回收。
public class LruCache<K, V> {
    private final LinkedHashMap<K, V> map;


// LruCache 內(nèi)存緩存 初始化操作
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;//總?cè)萘康拇笮楫?dāng)前最大內(nèi)存的八分之一

mLruCache = new LruCache<String, Bitmap>(cacheSize) {
    //計(jì)算緩存對(duì)象的大小
    @Override
    protected int sizeOf(String key, Bitmap bitmap) {
        //注意單位的轉(zhuǎn)換  bytes --> kb
        return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
    }

    //移除舊緩存時(shí)調(diào)用
    @Override
    protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
        super.entryRemoved(evicted, key, oldValue, newValue);
        // TODO: 2017/9/1 資源回收的工作
    }
};

DiskLruCache

DiskLruCache:磁盤緩存,通過將緩存對(duì)象寫入文件系統(tǒng)從而實(shí)現(xiàn)緩存效果

  • DiskLruCache的創(chuàng)建
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 應(yīng)用版本號(hào)
 * @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:表示一個(gè)緩存對(duì)象的編輯對(duì)象。最后記得commit()
    注意:因?yàn)閳D片的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獲取一個(gè)SnapShot對(duì)象,再使用Snapshot對(duì)象即可獲得緩存的文件輸入流。

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.Options 對(duì)FileInputStream進(jìn)行縮放會(huì)出現(xiàn)問題,因?yàn)?code>FileInputStream是一種有序的文件流,兩次decodeStream調(diào)用會(huì)影響文件流的位置屬性,導(dǎo)致第二次decodeStream時(shí)得到的是null。為了解決這個(gè)問題,可以通過文件流來得到它所對(duì)應(yīng)的文件描述符,然后通過BitmapFactory.decodeFileDescriptor來加載一張縮放后的圖片。

  • DiskLruCache.delete
  • DiskLruCache.remove

自定義ImageLoader的實(shí)現(xiàn)與圖片墻效果

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容