1.Bitmap高效加載
加載Bitmap的方法:使用BitmapFactory的decodeFile/decodeResource/decodeStream/decodeByteArray可以分別從,文件/資源/輸入流/字節數組中加載一個Bitmap。decodeFile/decodeResource會間接調用decodeStream。
通過採樣率控制加載出的Bitmap的大小,以提高加載效率:
- 將BitmapFactory.Options的inJustDecodeBounds參數設置爲true,並加載圖片
- 從BitmapFactory.Options中取出圖片的原始寬高信息,即outWidth和outHeight
- 根據採樣率的規則(2的倍數)以及目標View的大小,計算出inSampleSize的大小
- 將BitmapFactory.Options的inJustDecodeBounds參數設置爲false,重新加載圖片
public static Bitmap decodeSampleBitmapFromResource(Resources resource, int resId,
int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resource, resId, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(resource, resId, options);
}
public static int calculateInSampleSize (BitmapFactory.Options options, int reqWidth, int reqHeight) {
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
int halfWidth = width / 2;
int halfHeight = height / 2;
while (halfHeight / inSampleSize >= reqHeight
&& halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
2.Android中的緩存策略
LRU(Least Recently Used):內存緩存LruCache和存儲設備緩存DiskLruCache
- LruCache
內部採用一個LinkedHashMap,以強引用的方式存儲外界的緩存對象。使用方法,設置緩存大小,然後複寫sizeOf計算緩存對象的大小,之後通過put和get方法,添加和獲取緩存對象:
int cacheSize = 4 * 1024 * 1024; // 4MiB
LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
}}
如果緩存對象引用的資源需要顯式的進行釋放,可以複寫entryRemoved方法釋放資源。
當通過get方法成功訪問到一個緩存對象,在LruCache內部會將其移動到隊列頭部;通過put方法添加緩存時,如果緩存已滿,隊列最後一個對象就會被刪除。可以通過remove方法直接刪除一個緩存對象。
LruCache是線程安全的。
2. DiskLruCache
將緩存對象寫入文件系統進行緩存。不在sdk中,需另行下載。源碼
- 創建過程
通過DiskLruCache#open方法進行
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
throws IOException {
參數:
- directory:緩存目錄
- appVersion:應用版本號,版本號改變時會清空之前的緩存
- valueCount:單個key對應的數據個數
- maxSize:緩存空間大小
private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;
File diskCacheDir = getDiskCacheDir(mContext, "bitmap");
if (!diskCacheDir.exists()) {
diskCacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
- 添加緩存
DiskLruCache的緩存添加操作,通過Editor來完成。通過Editor得到一個輸出流:
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null) {
OutputStream out = editor.newOutputStream(0); // valueCount爲1,index從0開始
}
將數據寫入到該輸出流之後,調用editor的commit方法,緩存創建完成:
if (downloadUrlToStream(url, outputStream)) {
editor.commit();
}
- 獲取緩存
DiskLruCache的get方法獲取一個Snapshot對象,通過它便可得到輸入流,通過該輸入流便可得到緩存的文件。對於緩存Bitmap時的處理:因爲在加載大圖的時候,需要decode兩次,而輸入流讀取過一次,第二次就會返回null,對於這種情況,可以通過輸入流獲得文件的文件描述,然後調用BitmapFactory.decodeFileDescriptor來加載Bitmap:
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if (snapshot != null) {
FileInputStream in = (FileInputStream) snapshot.getInputStream(0);
FileDescriptor fd = in.getFD();
bitmap = decodeSampleBitmapFromFD(fd, reqWidth, reqHeight);
if (bitmap != null) {
addBitmapToMemoryCache(key, bitmap);
}
}