注意:本篇文章是本人阅读相关文章所写下的总结,方便以后查阅,所有内容非原创,侵权删。
本篇文章内容来自于:
1.Android开发艺术探索 任玉刚
2.Android图片缓存之Lru算法
目录
- 前言
--1.1 缓存策略一般缓存顺序为:内存-存储设备-网络
--1.2 缓存策略主要包含缓存的添加、获取和删除。(删除这步决定了要缓存算法)
--1.3 常用缓存算法LRU(LruCache和DiskLruCache) - LruCache缓存算法 内存缓存
--2.1 LruCache的使用 - DiskLruCache 磁盘缓存
--3.1 DiskLruCache的使用
1. 前言
1.1 缓存策略一般缓存顺序为:内存-存储设备-网络
缓存策略是一个通用的思想,可以用在很多场合。
通过缓存策略,我们不需要每次都从网络上请求图片,或者从存储设备中加载图片,这样就极大的提高了图片的加载效率以及产品的用户体验(减少流量消耗)。
具体做法为:
第一次从网络加载图片,将其缓存到存储设备上(这样下次使用这张图片不用再从网络上获取,节省流量),还会在内存中缓存一份(为提高应用的用户体验,因为内存中加载图片比从存储设备中加载图片要快)。
这样应用打算从网络上请求一张图片时:程序先从内存中去获取,如果内存中没有再到存储设备中去获取,最后再到网络上下载。
1.2 缓存策略主要包含缓存的添加、获取和删除。
缓存策略,没有统一的标准。
缓存策略主要包含缓存的添加、获取和删除。
为什么要删除呢?因为不管内存缓存还是存储设备缓存,缓存大小都是有限制的,因为内存和诸如SD卡之类的存储设备都是有容量限制的。因此使用缓存的时候总要为缓存指定一个最大的容量,当缓存容量满了,但程序还需要向其添加缓存,这个时候就需要删除一些旧的缓存并添加新的缓存。如何定义缓存的新旧就是一种策略,不同的策略对应不同的缓存算法。
1.3 常用缓存算法LRU(LruCache和DiskLruCache)
目前最常用的缓存算法是LRU,Lru是Least Recently Used的缩写,最近最少使用算法。这种算法的核心思想是:当缓存快满的时候,会淘汰近期最少使用的缓存目标。
采用LRU算法的缓存是LruCache和DiskLruCache
LruCache用于实现内存缓存,DiskLruCache用作存储设备缓存。
2. LruCache缓存算法
基于LruCache实现内存缓存
LruCache是Android3.1所提供的缓存类,用support-v4兼容包可以兼容到早期的Android版本。
LruCache是一个泛型类,内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象。
其提供了get和put方法来完成缓存的获取和添加操作,当缓存满的时候,LruCache会移除较早使用的缓存对象。然后再添加新的缓存对象。
LruCache是线程安全的。
强引用:直接的对象引用
软引用:当一个对象只有软引用存在时,系统内存不足的时候此对象会被gc回收。
弱引用:当一个对象只有弱引用存在时,此对象会随时被gc回收
2.1 LruCache的使用
1.LruCache的初始化
这里内存缓存的是Drawable 而不是Bitmap 理由是Drawable相对Bitmap来说有很大的内存优势
int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取系统分配给应用的总内存大小
int mCacheSize = maxMemory / 8;//设置图片内存缓存占用八分之一
mMemoryCache = new LruCache(mCacheSize) {
//必须重写此方法,来测量Bitmap的大小
@Override
protected int sizeOf(String key, Drawable value) {
if (value instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable) value).getBitmap();
return bitmap == null ? 0 : bitmap.getByteCount();
}
return super.sizeOf(key, value);
}
//某些特殊情况下,重写entryRemoved方法,LruCache移除缓存时会调用该方法,因此需要在这里完成一些资源回收工作。
};
2.添加一个Drawable到内存缓存
/**
* 添加Drawable到内存缓存
*
* @param key
* @param drawable
*/
private void addDrawableToMemoryCache(String key, Drawable drawable) {
if (getDrawableFromMemCache(key) == null && drawable != null) {
mMemoryCache.put(key, drawable);
}
}
3.从内存缓存中获取一个Drawable
/**
* 从内存缓存中获取一个Drawable
*
* @param key
* @return
*/
public Drawable getDrawableFromMemCache(String key) {
return mMemoryCache.get(key);
}
4.从内存缓存中移除一个Drawable
/**
* 从内存缓存中移除
*
* @param key
*/
public void removeCacheFromMemory(String key) {
mMemoryCache.remove(key);
}
5.清空内存缓存
/**
* 清理内存缓存
*/
public void cleanMemoryCCache() {
mMemoryCache.evictAll();
}
3. DiskLruCache
DiskLruCache用作存储设备缓存。它通过将缓存对象写入文件系统从而实现缓存的效果。
DiskLruCache类并不是谷歌官方实现,需要自行下载,下载地址:https://github.com/JakeWharton/DiskLruCache
3.1 DiskLruCache的使用
1. DiskLruCache的初始化
File cacheDir = context.getCacheDir();//指定的是数据的缓存地址,也可以选择SD卡上的缓存目录getExternalCacheDir()
long diskCacheSize = 1024 * 1024 * 30;//最多可以缓存多少字节的数据,超过则会清除缓存。这里设置成了30M
int appVersion = DiskLruUtils.getAppVersion(context);//指定当前应用程序的版本号,一般设置为1即可,当版本号发生改变时DiskLruCache会清空之前所有的缓存文件。但很多情况下即使改变,缓存文件也在。
int valueCount = 1;//指定同一个key可以对应多少个缓存文件
try {
mDiskCache = DiskLruCache.open(cacheDir, appVersion, valueCount, diskCacheSize);
} catch (Exception ex) {
}
1. 写入一个文件到磁盘缓存
/**
* 添加Bitmap到磁盘缓存
*
* @param key
* @param value
*/
private void addBitmapToDiskCache(String key, byte[] value) {
OutputStream out = null;
try {
//DiskLruCache的缓存操作通过Editor完成。
//Editor表示一个缓存对象的编辑对象,根据key就可以通过edit()获取Editor对象。
//如果这个缓存正在被编辑,那么edit()就会返回null,即DiskLruCache不允许同时编辑一个缓存对象。
DiskLruCache.Editor editor = mDiskCache.edit(key);
if (editor != null) {
//前面设置了一个节点只能有一个数据,则传入参数为0即可
out = editor.newOutputStream(0);
if (value != null && value.length > 0) {
out.write(value);
out.flush();
//必须通过commit来提交写入操作,否则不会真正的写入系统。
editor.commit();
} else {
//如果出现异常,则可以通过abort来回退整个操作。
editor.abort();
}
}
mDiskCache.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
DiskLruUtils.closeQuietly(out);
}
}
关于key的获取
一般根据图片url来获取对应的key
一般采用url的md5作为key,因为图片url可能有特殊字符。
private String hashKeyFromUrl(String url) {
String cacheKey;
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(url.getBytes());
cacheKey = bytesToHexString(digest.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();
}
2. 从磁盘缓存中读取Drawable
/**
* 从磁盘缓存中获取一个Drawable
*
* @param key
* @return
*/
public Drawable getDrawableFromDiskCache(String key) {
try {
DiskLruCache.Snapshot snapShot = mDiskCache.get(key);
if (snapShot != null) {
InputStream is = snapShot.getInputStream(0);
Bitmap bitmap = BitmapFactory.decodeStream(is);
Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);
//从磁盘中读取到之后 加入内存缓存
addDrawableToMemoryCache(key, drawable);
return drawable;
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
3. 从磁盘缓存中移除
/**
* 从磁盘缓存中移除
*
* @param key
*/
public void removeCacheFromDisk(String key) {
try {
mDiskCache.remove(key);
} catch (Exception e) {
}
}
4. 清空磁盘缓存
/**
* 清理磁盘缓存
*/
public void cleanDiskCache() {
try {
mDiskCache.delete();
} catch (Exception e) {
}
}