1、磁盘缓存 DiskLruCache
参考链接 https://blog.youkuaiyun.com/guolin_blog/article/details/28863651
记录总结:防止多图OOM的核心解决思路就是使用LruCache技术。但LruCache只是管理了内存中图片的存储与释放,如果图片从内存中被移除的话,那么又需要从网络上重新加载一次图片,这显然非常耗时。因此使用到了DiskLruCache进行硬盘/磁盘缓存
磁盘缓存的好处:在没有网络的情况下或者内存中被移除的情况下,依然可以快速加载图片。
DiskLruCache并没有限制数据的缓存位置,可以自由地进行设定,但是通常情况下多数应用程序都会将缓存的位置选择为 /sdcard/Android/data/<application package>/cache 这个路径。选择在这个位置有两点好处:第一,这是存储在SD卡上的,因此即使缓存再多的数据也不会对手机的内置存储空间有任何影响,只要SD卡空间足够就行。第二,这个路径被Android系统认定为应用程序的缓存路径,当程序被卸载的时候,这里的数据也会一起被清除掉,这样就不会出现删除程序之后手机上还有很多残留数据的问题。
创建DiskLruCache实例,需要调用的是open方法:
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
获取缓存地址的方法:
- 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);
- }
因此,一个非常标准的open()方法就可以这样写:
- DiskLruCache mDiskLruCache = null;
- try {
- File cacheDir = getDiskCacheDir(context, "bitmap");
- if (!cacheDir.exists()) {
- cacheDir.mkdirs();
- }
- mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
- } catch (IOException e) {
- e.printStackTrace();
- }
写入的操作:不能new对象,写入的操作是借助DiskLruCache.Editor这个类完成的。
public Editor edit(String key) throws IOException
edit()方法接收一个参数key,这个key将会成为缓存文件的文件名,并且必须要和图片的URL是一一对应的。那么怎样才能让key和图片的URL能够一一对应呢?直接使用URL来作为key?不太合适,因为图片URL中可能包含一些特殊字符,这些字符有可能在命名文件时是不合法的。其实最简单的做法就是将图片的URL进行MD5编码,编码后的字符串肯定是唯一的,并且只会包含0-F这样的字符,完全符合文件的命名规则。
一次完整写入操作的代码如下所示:
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- String imageUrl = "https://img-my.youkuaiyun.com/uploads/201309/01/1378037235_7476.jpg";
- String key = hashKeyForDisk(imageUrl);
- DiskLruCache.Editor editor = mDiskLruCache.edit(key);
- if (editor != null) {
- OutputStream outputStream = editor.newOutputStream(0);
- if (downloadUrlToStream(imageUrl, outputStream)) {
- editor.commit();
- } else {
- editor.abort();
- }
- }
- mDiskLruCache.flush();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }).start();
注意在代码的最后我还调用了一下flush()方法,这个方法并不是每次写入都必须要调用的。一般在activity的onpause方法中调用。
读取缓存:读取的方法要比写入简单一些,主要是借助DiskLruCache的get()方法实现的
public synchronized Snapshot get(String key) throws IOException
读取缓存数据的代码就可以这样写:
- String imageUrl = "https://img-my.youkuaiyun.com/uploads/201309/01/1378037235_7476.jpg";
- String key = hashKeyForDisk(imageUrl);
- DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
这里获取到的是一个DiskLruCache.Snapshot对象,这个对象我们该怎么利用呢?很简单,只需要调用它的getInputStream()方法就可以得到缓存文件的输入流了。此时,即使在没有网络的情况下,也可以加载显示图片了。
移除缓存:DiskLruCache会根据我们在调用open()方法时设定的缓存最大值来自动删除多余的缓存。只有你确定某个key对应的缓存内容已经过期,需要从网络获取最新数据的时候才应该调用remove()方法来移除缓存。
DiskLruCache中使用了一个redundantOpCount变量来记录用户操作的次数,每执行一次写入、读取或移除缓存的操作,这个变量值都会加1,当变量值达到2000的时候就会触发重构journal的事件,这时会自动把journal中一些多余的、不必要的记录全部清除掉,保证journal文件的大小始终保持在一个合理的范围内。