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可能含有特殊字元,所以一般採用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獲取一個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.Options
對FileInputStream
進行縮放會出現問題,因為FileInputStream
是一種有序的檔案流,兩次decodeStream
呼叫會影響檔案流的位置屬性,導致第二次decodeStream
時得到的是null
。為了解決這個問題,可以通過檔案流來得到它所對應的檔案描述符,然後通過BitmapFactory.decodeFileDescriptor
來載入一張縮放後的圖片。
- DiskLruCache.delete
- DiskLruCache.remove