Glide框架手写实现(一)资源封装
Glide框架手写实现(二)活动缓存
Glide框架手写实现(三)内存缓存
Glide框架手写实现(四)磁盘缓存
Glide框架手写实现(五)生命周期
上一篇文章,我们封装了活动缓存和内存缓存,现在我们要先明确一个概念,不管是活动缓存还是内存缓存,只有在app运行的时候,才会生效,如果你把app杀死了,这两个缓存就没用了,所以才引出我们今天的主题“磁盘缓存”
。磁盘缓存是保存在sd卡中的,所以就算你app杀死了,下次启动还是可以去找到里面的图片的。
image.png
解释一下glide框架加载图片到imageview的流程:一个app启动,里面有个imageview要加载图片,首先会去活动缓存中去查找,如果找到了,直接返回给app加载,如果找不到,去内存缓存中找,找到了,会先从内存缓存中加载到活动缓存中,app再从活动缓存中去加载,(活动缓存和内存缓存是互斥的,图片对象在二者中只能存在一个,不明白的去看上一篇文章),如果内存缓存中还是找不到,才会去磁盘缓存中去查找,找到了,也会先从磁盘缓存中加载到活动缓存中,app再从活动缓存中去加载,如果磁盘缓存还是找不到,才会去调用外部资源加载(网络加载或者本地加载)。
这就是图片框架加载的整个流程。
流程先讲到这里,等我们把全部的“砖块”都封装好,去用我们手写的glide框架真正加载图片的时候,我们再具体讲一下流程(后续文章中),现在我们先进入这篇文章的正题,开始封装磁盘缓存。
我们磁盘缓存的封装,是在DiskLruCache的基础上进行的,DiskLruCache大家都知道,是android之神JakeWharton在2011年写的一个框架,在github上可以找到,
https://github.com/JakeWharton/DiskLruCache/tree/master/src/main/java/com/jakewharton/disklrucache,
DiskLruCache的回收机制也是Lru最小使用算法,只是他把数据存放在了本地sd中(磁盘),不是存放在内存中而已。我们把它的代码down下来放入自己的工程里,然后真正开始我们的步骤。
/**
* 我们自己的 磁盘缓存的封装
* 目的:put 把我们的Value 存储进去
* get 通过key 得到 Value
*/
public class DiskLruCacheImpl {
private final String TAG = DiskLruCacheImpl.class.getSimpleName();
// Sdcard/disk_lru_cache_dir/ac037ea49e34257dc5577d1796bb137dbaddc0e42a9dff051beee8ea457a4668/缓存东西(Value)
private final String DISKLRU_CACHE_DIR = "disk_lru_cache_dir"; // 磁盘缓存的的目录
private final int APP_VERSION = 1; // 我们的版本号,一旦修改这个版本号,之前的缓存失效
private final int VALUE_COUNT = 1; // 通常情况下都是1
private final long MAX_SIZE = 1024 * 1024 * 10; // 以后修改成 使用者可以设置的
private DiskLruCache diskLruCache;
public DiskLruCacheImpl() {
// SD 路径
File file = new File(Environment.getExternalStorageDirectory() + File.separator + DISKLRU_CACHE_DIR);
try {
diskLruCache = DiskLruCache.open(file, APP_VERSION, VALUE_COUNT, MAX_SIZE);
} catch (IOException e) {
e.printStackTrace();
}
}
// TODO put
public void put(String key, Value value) {
Tool.checkNotEmpty(key);
DiskLruCache.Editor editor = null;
OutputStream outputStream = null;
try {
editor = diskLruCache.edit(key);
outputStream = editor.newOutputStream(0);// index 不能大于 VALUE_COUNT
Bitmap bitmap = value.getmBitmap();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); // 把bitmap写入到outputStream
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
// 失败
try {
editor.abort();
} catch (IOException e1) {
e1.printStackTrace();
Log.e(TAG, "put: editor.abort() e:" + e.getMessage());
}
} finally {
try {
editor.commit(); // sp 记得一定要提交
diskLruCache.flush();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "put: editor.commit(); e:" + e.getMessage());
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "put: outputStream.close(); e:" + e.getMessage());
}
}
}
}
// TODO get
public Value get(String key) {
Tool.checkNotEmpty(key);
InputStream inputStream = null;
try {
DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
// 判断快照不为null的情况下,在去读取操作
if (null != snapshot) {
Value value = Value.getInstance();
inputStream = snapshot.getInputStream(0);// index 不能大于 VALUE_COUNT
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
value.setmBitmap(bitmap);
// 保存key 唯一标识
value.setKey(key);
return value;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "get: inputStream.close(); e:" + e.getMessage());
}
}
}
return null; // 为了后续好判断
}
}
DISKLRU_CACHE_DIR是我们要存放图片的sd卡的目录,APP_VERSION、VALUE_COUNT、MAX_SIZE这三个成员变量,在使用DiskLruCache时会使用到,APP_VERSION代表版本号,这个不要乱改动,一旦你开始使用,然后再去改它的值,之前的缓存会失效的,MAX_SIZE就是你可以最大缓存的大小。在DiskLruCacheImpl的构造函数里,我们new一个file,传入sd卡目录,
然后调用DiskLruCache的open方法。像上两节一样,磁盘缓存也需要添加和移除方法,就是我们上面的put方法和get方法。(put和get方法是DiskLruCache的基本使用,我这里就不讲了,网上很多教程),我们这样讲一下get方法里的这段代码
Value value = Value.getInstance();
inputStream = snapshot.getInputStream(0);// index 不能大于 VALUE_COUNT
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
value.setmBitmap(bitmap);
// 保存key 唯一标识
value.setKey(key);
这段代码就是拿到DiskLruCache里最终的bitmap,存到我们自己封装的Value对象里。
磁盘缓存我们就封装到这里。大家主要是了解活动缓存,内存缓存,磁盘缓存各自的用处。下一节我们开始实现glide的生命周期的封装。
网友评论