這也是一個個人的學習筆記吧,瞭解了一下三級緩存一般是如何實現的。當然寫了這個我也不會用在自己項目中,因爲是Glide它不香了還是Fresco不好用??前面說下需要了解的一些知識,後面全是代碼。
首先,圖片編碼:
- ALPHA_8 圖片只有alpha值,沒有RGB值,一1個像素佔用一個字節
- ARGB_4444 alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各佔4個bites共16bites,2個字節
- ARGB_8888 alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各佔8個bites,共32bites,4個字節。
- RGB_565 沒有alpha(A)值,不支持透明和半透明,Red(R)值佔5個bites ,Green(G)值佔6個bites ,Blue(B)值佔5個bites,共16bites,2個字節。
基本上常用的就這些。系統默認的是ARGB_8888也就是默認一個像素點4字節,不需要透明度的時候可以手動切成RGB_565。
LruCache原理
簡而言之就是內部維護了一個雙向鏈表,LinkedHashMap。會先設定一個緩存大小,一般爲可用內存的八分之一。如果有新數據插入,會把新數據插到隊尾,所以頭部的數據就是最近最少使用的數據。插入之後如果內存大小超過限制了,那就會移除頭部元素。
InBitmap
需要事先設置Inmutable爲true。Inmutable是表示這個bitmap的內存大小可變。我們用Bitmap工廠創建一個Bitmap後,都會從內存獲得一塊空間。InBitmap就是讓後面的Bitmap直接複用之前的Bitmap空間,所以這個內存空間大小必須是可以變動的才行。
但是使用有很多限制,官網上有直接給出代碼,抄就行了。
- 4.4之前的版本inBitmap只能夠重用相同大小的Bitmap內存區域。簡單而言,被重用的Bitmap需要與新的Bitmap規格完全一致,否則不能重用。
- 4.4之後的版本系統不再限制舊Bitmap與新Bitmap的大小,保證舊Bitmap的大小是大於等於新Bitmap大小即可。
- 舊Bitmap必須是mutable的
三級緩存
- 首次加載的時候通過網絡加載,獲取圖片,然後保存到內存和 SD 卡中。
- 運行 APP 時,優先訪問內存中的圖片緩存。
- 如果內存沒有,則加載本地 SD 卡中的圖片。
內存->磁盤->網絡。加載速度和順序就是這樣。
磁盤緩存
DiskLruCache,直接在GitHub上可以下載到,也算是google官方推薦的。剩下的直接上代碼了。
/**
*/
public class ImageManager {
//磁盤緩存
private DiskLruCache diskLruCache;
private LruCache<String, Bitmap> lruCache;
//複用池
private Set<SoftReference<Bitmap>> reusableBitmaps;
//引用隊列
private ReferenceQueue<Bitmap> referenceQueue;
//取出線程
private Thread clearThread;
private static volatile ImageManager imageManager;
public static ImageManager getInstance() {
if (imageManager == null) {
synchronized (ImageManager.class) {
if (imageManager == null) {
imageManager = new ImageManager();
}
}
}
return imageManager;
}
public void init(Context context) {
ActivityManager am = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
lruCache = new LruCache<String, Bitmap>(am.getMemoryClass() / 8 * 1024 * 1024) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if (oldValue.isMutable()) {
reusableBitmaps.add(new SoftReference<Bitmap>(oldValue, referenceQueue));
} else {
oldValue.recycle();
}
}
};
reusableBitmaps =
Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());
clearThread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Reference remove = referenceQueue.remove();
Bitmap bitmap = (Bitmap) remove.get();
if (bitmap != null) {
bitmap.recycle();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
clearThread.start();
referenceQueue = new ReferenceQueue();
File file = getDiskCacheDir(context, "thumb");
try {
diskLruCache = DiskLruCache.open(file, BuildConfig.VERSION_CODE, 1,
10 * 1024 * 1024);
} catch (IOException e) {
}
}
/**
* 寫進內存
*
* @param key
* @param bitmap
*/
public void putToMemory(String key, Bitmap bitmap) {
lruCache.put(key, bitmap);
}
/**
* 從複用池獲取
*/
public Bitmap getFromPool() {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB) {
return null;
}
Bitmap getInBitmap = null;
Iterator<SoftReference<Bitmap>> iterator = reusableBitmaps.iterator();
while (iterator.hasNext()) {
SoftReference<Bitmap> next = iterator.next();
Bitmap bitmap = next.get();
if (bitmap != null) {
getInBitmap = bitmap;
break;
}
}
return getInBitmap;
}
/**
* 從內存獲取
*
* @param key
* @return
*/
public Bitmap getFromMemory(String key) {
return lruCache.get(key);
}
/**
* 寫入磁盤的方法
*
* @param key
* @param bitmap
*/
public void putToDisk(String key, Bitmap bitmap) {
DiskLruCache.Snapshot snapshot = null;
try {
snapshot = diskLruCache.get(key);
//如果磁盤沒有文件,寫入
if (snapshot == null) {
DiskLruCache.Editor edit = diskLruCache.edit(key);
OutputStream outputStream = edit.newOutputStream(0);
bitmap.compress(Bitmap.CompressFormat.JPEG, 0, outputStream);
edit.commit();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (snapshot != null) {
snapshot.close();
}
}
}
BitmapFactory.Options options = new BitmapFactory.Options();
public Bitmap getFromDisk(String key, Bitmap bitmap) {
Bitmap diskBitmap = null;
DiskLruCache.Snapshot snapshot = null;
try {
snapshot = diskLruCache.get(key);
if (snapshot == null) {
return null;
}
InputStream inputStream = snapshot.getInputStream(0);
options.inMutable = true;
options.inBitmap = bitmap;
diskBitmap=BitmapFactory.decodeStream(inputStream, null, options);
putToMemory(key, diskBitmap);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (snapshot != null) {
snapshot.close();
}
}
return diskBitmap;
}
public void clearAll() {
lruCache.evictAll();
}
public File getDiskCacheDir(Context context, String uniqueName) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + uniqueName);
}
}
這就是三級緩存的管理類,從內存到磁盤。要注意的地方不多,大部分地方也加上了註釋。缺陷點的地方就是InBitmap的使用。
如果有什麼不對的地方可以在評論區指出謝謝。