概述
缓存策略的主要流程:
当程序第一次从网络加载图片后,将其缓存到储存设备上,下一次就不用再次从网络上获取了。为了提高应用的用户体验,往往还会再内存中再缓存一份,这样当应用打算从网络请求一张图片时,首先从内存中读取,如果没有那就从储存设备中获取,如果储存设备也没有,那就从网络上下载这张图片。因为从内存中加载图片比储存设备加载要快,所以这样既提高程序的效率又为用户节约了不必要的流量开销。而这种缓存策略不仅仅适用于图片,也适用于其他文件类型。
缓存算法
目前常用的一种缓存算法是LRU(Least Recently Used),LRU是近期最少使用的算法,核心思想:当缓存满时,会优先淘汰那些近期最少使用的缓存对象。采用LRU算法的缓存有两种:LruCache和DiskLruCache,LruCache用于实现内存缓存,而DiskLruCaChe则充当了存储设备缓存。
LruCache
LruCache是一个泛型类,内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象。并且提供了get和put方法来完成缓存的获取和添加操作。
- 强引用:直接的对象引用
- 软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被gc回收
- 弱引用:当一个对象只有弱引用存在时,此对象会被随时gc回收
初始化LruCaChe
int MaxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize){
@override
protected int sizeof(String key,Bitmap bitmap){
return bitmap.getRowBytes()*bitmap.getHeight() / 1024;
}
}
在上面代码中,只需要提供缓存的总容量大小并重写sizeof方法即可。另外一些特殊情况下,还需要重写LruCache的entryRemoved方法,LruCache移除旧缓存时会调用entryRemoved方法,因此可以在entryRemoved中完成一些资源回收工作。
从LruCache中获取一个缓存对象
mMemoryCache.get(key);
向LruCache中添加一个缓存对象
mMemoryCache.put(key,bitmap)
LruCache还支持删除操作,通过remove方法即可删除一个指定的缓存对象。
DiskLruCache
1.DiskLruCache的创建
DiskLruCache提供了open方法创建
public static DiskLruCache open(File directory,int appVersion,int valueCount,long maxSize);
open方法有四个参数,其中第一个参数为磁盘缓存在文件系统中的储存路径。缓存可以选择SD卡上的缓存目录,具体为:***/sdcard/Android/data/package_name/cache***目录,当应用被卸载后,包名的目录会一并被删除。当然还可以选择sd卡上的其他指定路径,还可以选择data下的当前应用的目录。这里一般有个原则:如果应用卸载后就希望删除缓存文件,那么就选择SD卡上的缓存目录,如果希望保留缓存数据那就应该选择sd卡上的其他路径
第二个参数为版本号,一般设为1.当版本号改变时会清空之前的所以缓存文件。
第三个参数表示单个节点所对应的数据的个数,一般设为1即可。第四个参数为缓存的总大小。下面是典型的DiskLruCache的创建过程:
private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50 ;//50Mb
File diskCacheFile = getDiskCacheDir(mContext,"bitmap");
if(!diskCacheFile.exists()){
dislCacheFile.mkdirs();
}
mDiskLruCache = DiskLruCache.open(diskCaCheFile,1,1,DISK_CACHE_SIZE);
2.DiskLruCache的缓存添加
DiskLruCache的缓存添加的操作是通过Editor完成的,Editor表示一个缓存对象的编辑对象。这里以照片缓存为例子,首先获取图片的URL所对应的key,然后根据key就可以通过edit()来获取Editor对象,如果这个缓存正在被编辑,那么edit()会返回null,即DiskLruCache不允许同时编辑一个对象。之所以要把url转化成key,是因为图片的url很可能含有特殊的字符,这会影响url在安卓中直接使用,一般采用url的md5值作为key;
private String hashKeyFormUrl(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(OxFF & bytes[i]);
if(hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
将图片的url转成key后,就可以获得Editor对象了。对于key来说,如果当前不存在其他的Editor对象,那么edit()就会返回一个新的Editor对象,通过他可以获得文件输出流。需注意前面的diskLruCache的open方法中设置了一个节点只有一个数据,因此下面的DISK_CACHE_INDEX常量直接设置成0即可。
String key = hashkeyFormUrl(url);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if(editor != null) {
OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
}
有了文件输出流,那么当从网络下载图片时,图片就可以通过这个文件输出流写入到文件系统上,具体实现过程如下:
public boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
HttpURLConnection urlConnection = null;
BufferedOutputStream out = null;
BufferedInputSream in = null;
try {
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream(urlConnection.getInputSream(),IO_BUFFER_SIZE);
out = new BufferedOutputStream(outputStream,IO_BUFFER_SIZE);
int b;
while ((b = in.read())!= -1) {
out.write(b);
}
return ture;
} catch (IOException e) {
e.printStack();;
}finally {
if(urlConnection != null) {
urlConnection.disConnect();
}
MyUtils.close(in);
MyUtils.close(out);
}
return false;
}
另外,必须通过Editor的commit()来提交写入操作,如果照片下载过程发生了异常,还可以通过Editor的abort()来回退整个操作。
OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
if(downloadUrlTOSream(url,outputStream)) {
editor.commmit();
} else {
editor.abort();
}
mDiskLruCache.flush();
3.DiskLruCache的缓存查找
缓存查找的过程首先需要将url转化成key,然后通过DiskLruCache的get方法得到一个Snapshot对象,再接着通过这个对象即可获得缓存文件的输入流。进而得到Bitmap对象。为了避免加载图片导致的OOM,一般不建议直接加载原图。上文提到使用BitmapFactory.Options来加载缩放后的图片,但是这种方法之前也提到对FileInputSream存在问题,原因是FileOutput是一种有序的文件流,两次的decodeStream调用会影响文件流的位置属性,导致第二次调用decodeStream时返回了null.故为了解决这种问题,首先得获得文件描述符,再通过BitmapFactory.decodeFileDecriptor方法加载一张缩放后的照片。
Bitmap bitmap = null;
String key = hashKeyFormUrl(url);
DislLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if(snapShot != null) {
FileInputStream fileInputStream = (FileInputStream) snapShot.getInputSream(DISK_CACHE_INDEXT);
FileDecriptor fileDecriptor = fileInputStream.getFD();
bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDecriptor,reqWidth,reqHeight);
if(bitmap != null) {
addBitmapToMemoryCache(key,bitmap);
}
}
注:本笔记来源于《Android开发艺术探索》相关知识
最后
以上就是独特向日葵为你收集整理的Android中的缓存策略的全部内容,希望文章能够帮你解决Android中的缓存策略所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复