1. 整體思路設計
採用三級緩存結構:內存-磁盤-網絡,緩存使用的是LruCache算法,最近最少使用緩存算法
- 內存緩存使用API自帶實現的LruCache來滿足
- 磁盤緩存使用官方推薦的DiskLruCache來滿足
- 內存資源比較珍貴,在LruCache的基礎上,增加了複用池及回收隊列來提高效率
2. 分塊解析
A. 獲取結構
這部分結構比較簡單,依次取出,爲空時進行資源獲取,同時對獲取的資源進行大小優化、內存複用
/**
* 返回壓縮圖片
*
* @param context
* @param filePath
* @param maxW
* @param maxH
* @param hasAlpha
* @return
*/
public static Bitmap resizeBitmap(Context context, String filePath, int maxW, int maxH, boolean hasAlpha, Bitmap reusable) {
BitmapFactory.Options options = new BitmapFactory.Options();
// 設置爲true後,再去解析,就只解析 out 參數
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
int w = options.outWidth;
int h = options.outHeight;
options.inSampleSize = calcuteInSampleSize(w, h, maxW, maxH);
if (!hasAlpha) {
options.inPreferredConfig = Bitmap.Config.RGB_565;
}
options.inJustDecodeBounds = false;
// 複用, inMutable 爲true 表示易變
options.inMutable = true;
options.inBitmap = reusable;
return BitmapFactory.decodeFile(filePath, options);
}
大小優化:按需解析(寬高、是否需要透明度)
內存複用:使用可複用的內存(inBitmap),並將自身設置可變(inMutable)
B. 緩存結構拆分
磁盤緩存並不需要額外的優化,所以此處最主要是對內存資源的利用,這部分的利用分兩方面:
-
內存複用:複用池、Bitmap的複用開關
內存資源的開闢消耗是比較大的,對於已經申請的內存資源直接複用起來是比較好的提升手段( 優化釋放舊Bitmap內存以及重新申請Bitmap內存導致的性能損耗 );通過複用池,在緩存空間被用完時,對將移除釋放的緩存進行復用池複用,在下一次新加入緩存時直接利用這塊內存空間
-
內存回收效率提升:複用池、弱引用、阻塞隊列
GC在工作時,會掃描到複用池中不被使用的對象,並標記;此時,弱引用會將被標記的對象放入配置的阻塞隊列,阻塞隊列對該對象進行主動回收(效率高於GC)
3. 功能點解析
關聯:緩存+複用池+弱引用+回收隊列
//內存緩存 大小一般默認取當前應用內存的1/8
lruCache = new LruCache<String, Bitmap>(SystemUtils.getAppMemoryForByte(context) / 8) {
@Override
protected int sizeOf(String key, Bitmap value) {
return BitmapUtils.getBitmapSize(value);
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if (oldValue.isMutable()) {
//將移除對象與阻塞隊列配置爲弱引用對象放入複用池(WeakReference在oldValue標記回收時會被添加到 設置的引用隊列中)
reusablePool.add(new WeakReference<Bitmap>(oldValue, getReferenceQueue()));
}else {
oldValue.recycle();
}
}
};
複用池的獲取:
/**
* 複用 指使用 重新使用 之前bitmap佔用的內存塊
* 3.0 之前不能複用
* 3.0-4.4 寬高一樣,inSampleSize = 1 自己複用自己
* 4.4 只要小於等於就行了 小的可以使用大的
*
* @param w
* @param h
* @param inSampleSize
* @return
*/
public Bitmap getReusable(int w, int h, int inSampleSize) {
//3.0 以下沒有複用功能
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return null;
}
Bitmap reusable = null;
Iterator<WeakReference<Bitmap>> iterator = reusablePool.iterator();
while (iterator.hasNext()) {
Bitmap bitmap = iterator.next().get();
if (bitmap != null) {
if (checkInBitmap(bitmap, w, h, inSampleSize)) {
reusable = bitmap;
iterator.remove();
break;
}
} else {
iterator.remove();
}
}
return reusable;
}
/**
* 校驗bitmap 是否滿足複用條件
*
* @param bitmap
* @param w
* @param h
* @param inSampleSize
* @return
*/
private boolean checkInBitmap(Bitmap bitmap, int w, int h, int inSampleSize) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return bitmap.getWidth() == w && bitmap.getHeight() == h && inSampleSize == 1;
}
if (inSampleSize > 1) {
w /= inSampleSize;
h /= inSampleSize;
}
int byteCount = w * h * getBytesPerPixel(bitmap.getConfig());
// 圖片內存 系統分配內存
return byteCount <= bitmap.getAllocationByteCount();
}
阻塞隊列:主動回收
private ReferenceQueue<Bitmap> getReferenceQueue() {
if (referenceQueue == null) {
referenceQueue = new ReferenceQueue<>();
new Thread(new Runnable() {
@Override
public void run() {
while (!shutDown) {
try {
//remove帶阻塞功能,
Reference<? extends Bitmap> remove = referenceQueue.remove();
Bitmap bitmap = remove.get();
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
return referenceQueue;
}