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可能含有特殊字符,所以一般采用url
的md5
值做為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