Volley ImageLoader Memory-Disk兩級Cache的實現
阿新 • • 發佈:2019-01-05
暮鼓集 行走集
原作於2017年01月01日
Volley的ImageLoader可以自定義快取機制,但是官方只給出了Memory Cache的實現,這樣,一旦應用被關閉,Cache Memory被系統回收,下次再開啟時,仍然需要請求網路來重新獲取。 在實際開發中,很多應用是希望將快取持久化到Disk中。但是,單純的將Memory Cache修改為Disk Cache,會帶來效率的問題,而Memory-Disk兩級快取可以有效解決。
實現的思路是使用LruCache構建Memory Cache,使用DiskLruImageCache實現Disk Cache。 在寫快取時,同時寫入到Memory與Disk中, 在讀快取時,先在Memory中檢索,如果沒有命中再在Disk中檢索。
具體實現如下,包括三個檔案。
ImageCache.java
public class ImageCache implements ImageLoader.ImageCache{ private static final int DEFAULT_MAX_MEM_ENTRIES = 50; private static final int DEFAULT_MAX_DISK_SIZE = 20 * 1024 * 1024; private LruCache<String, Bitmap> mLruCache; // L1 Cache private DiskLruImageCache mDiskLruCache; // L2 Cache public ImageCache( int maxMemEntries, String cacheDir, int maxDiskSize ){ mLruCache = new LruCache<String, Bitmap>(maxMemEntries); mDiskLruCache = new DiskLruImageCache(cacheDir, maxDiskSize, Bitmap.CompressFormat.JPEG, 70); } @Override public Bitmap getBitmap(String url) { Bitmap bmp = mLruCache.get(url); if( bmp != null ) return bmp; bmp = mDiskLruCache.get(key(url)); return bmp; } @Override public void putBitmap(String url, Bitmap bitmap) { if( bitmap == null ) return; mLruCache.put(url, bitmap); mDiskLruCache.put(key(url), bitmap);; } public String key(String url) { String cacheKey; try { final MessageDigest mDigest = MessageDigest.getInstance("MD5"); mDigest.update(url.getBytes()); cacheKey = bytesToHexString(mDigest.digest()); } catch (NoSuchAlgorithmException e) { cacheKey = String.valueOf(url.hashCode()); } return cacheKey; } private String bytesToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(0xFF & bytes[i]); if (hex.length() == 1) { sb.append('0'); } sb.append(hex); } return sb.toString(); } }
DiskLruImageCache.java
public class DiskLruImageCache { private DiskLruCache mDiskCache; private CompressFormat mCompressFormat = CompressFormat.JPEG; private static int IO_BUFFER_SIZE = 8 * 1024; private int mCompressQuality = 70; private static final int APP_VERSION = 1; private static final int VALUE_COUNT = 1; public DiskLruImageCache(String cacheDir, int diskCacheSize, CompressFormat compressFormat, int quality) { try { final File diskCacheDir = new File(cacheDir); mDiskCache = DiskLruCache.open(diskCacheDir, APP_VERSION, VALUE_COUNT, diskCacheSize); mCompressFormat = compressFormat; mCompressQuality = quality; } catch (IOException e) { e.printStackTrace(); } } private boolean writeBitmapToFile(Bitmap bitmap, DiskLruCache.Editor editor) throws IOException, FileNotFoundException { OutputStream out = null; try { out = new BufferedOutputStream(editor.newOutputStream(0), IO_BUFFER_SIZE); return bitmap.compress(mCompressFormat, mCompressQuality, out); } finally { if (out != null) { out.close(); } } } private File getDiskCacheDir(Context context, String uniqueName) { final String cachePath = context.getCacheDir().getPath(); return new File(cachePath + File.separator + uniqueName); } public void put(String key, Bitmap data) { DiskLruCache.Editor editor = null; try { editor = mDiskCache.edit(key); if (editor == null) { return; } if (writeBitmapToFile(data, editor)) { mDiskCache.flush(); editor.commit(); if (BuildConfig.DEBUG) { Log.d("cache_test_DISK_", "image put on disk cache " + key); } } else { editor.abort(); if (BuildConfig.DEBUG) { Log.d("cache_test_DISK_", "ERROR on: image put on disk cache " + key); } } } catch (IOException e) { if (BuildConfig.DEBUG) { Log.d("cache_test_DISK_", "ERROR on: image put on disk cache " + key); } try { if (editor != null) { editor.abort(); } } catch (IOException ignored) { } } } public Bitmap get(String key) { Bitmap bitmap = null; DiskLruCache.Snapshot snapshot = null; try { snapshot = mDiskCache.get(key); if (snapshot == null) { return null; } final InputStream in = snapshot.getInputStream(0); if (in != null) { final BufferedInputStream buffIn = new BufferedInputStream(in, IO_BUFFER_SIZE); bitmap = BitmapFactory.decodeStream(buffIn); } } catch (IOException e) { e.printStackTrace(); } finally { if (snapshot != null) { snapshot.close(); } } if (BuildConfig.DEBUG) { Log.d("cache_test_DISK_", bitmap == null ? "" : "image read from disk " + key); } return bitmap; } }
DiskLruCache.java
官方連結
https://android.googlesource.com/platform/libcore/+/android-4.1.1_r1/luni/src/main/java/libcore/io/DiskLruCache.java