Volley ImageLoader Memory-Disk两级Cache的实现

暮鼓集    行走集

原作于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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值