暮鼓集 行走集
原作于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