本篇文章已授权微信公众号 顾林海 独家发布
通过缓存策略,我们不需要每次都从网上请求图片或从存储设备中加载图片,这样就极大地提高了图片的加载效率以及用户体验。目前比较常用的缓存策略是LruCache和DiskLruCache,其中LruCache被称为内存缓存,DiskLruCache被称为存储缓存。Lru是Least Recently Used的缩写,即最近最少使用算法,算法核心思想是:当缓存快满时,会淘汰近期最少使用的缓存目标。
缓存路径:内存->存储设备->网络
LruCache
LruCache是一个泛型类,它内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,其提供了get和put方法来完成缓存的获取和添加操作,当缓存满时,LruCache会移除较早的缓存对象,然后再添加新的缓存对象。
LruCache是线程安全的。
强引用、软引用和弱引用的区别:
强引用:直接的对象引用。
软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被GC回收。
弱引用:当一个对象只有弱引用存在时,此对象随时被GC回收。
LruCache初始化代码:
private LruCache mMemoryCache;
private void lruCache(){
//当进程可用的最大内存(KB)
int maxMemory= (int) (Runtime.getRuntime().maxMemory()/1024);
//LruCache缓存的总容量
int cacheSize=maxMemory/8;
mMemoryCache=new LruCache(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes()*value.getHeight()/1024;
}
};
}
重写sizeOf方法,用来计算缓存对象的大小。
在一些特殊情况下,还需要重写LruCache的entryRemoved方法,LruCache移除旧缓存时会调用entryRemoved方法,可以在entryRemoved方法中做一些资源回收工作。
DiskLruCache
DiskLruCache用于实现存储设备缓存,即磁盘缓存,它通过将缓存对象写入文件系统从而实现缓存的效果。
引入:
compile 'com.jakewharton:disklrucache:2.0.2'
DiskLruCache不能通过构造方法来创建,它提供了open方法用于创建自身,如下代码:
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
open方法有四个参数,第一个参数表示磁盘缓存在文件系统中的存储路径。缓存路径可以选中SD卡上的缓存目录,具体是指/sdcard/Android/data/package_name/cache 目录,当应用被卸载后,此目录会一并删除,也可以存储在其他指定目录。
第二个参数表示应用的版本号,一般设为1即可。当版本号发生改变时DiskLruCache会清空之前所有的缓存文件。
第三个参数表示单个节点所对应的数据的个数,一般设为1即可。
第四个参数表示缓存的总大小。
DiskLruCache的创建:
private DiskLruCache mDiskLruCache;
private static final long DISK_CACHE_SIZE=1024*1024*50;
private void diskLruCache(){
File diskCacheDir=new File(getCacheDir().getPath()+"/bitmap");
if(!diskCacheDir.exists()){
diskCacheDir.mkdirs();
}
try {
mDiskLruCache=DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE);
} catch (IOException e) {
e.printStackTrace();
}
}
DiskLruCache的缓存添加的操作是通过Editor完成的,Editor表示一个缓存对象的编辑对象。
DiskLruCache的缓存添加:
private void diskCache(String url){
String key=hashKeyFormUrl(url);
try {
DiskLruCache.Editor editor=mDiskLruCache.edit(key);
if(editor!=null){
OutputStream outputStream=editor.newOutputStream(0);
if(downloadUrlToStream(url,outputStream)){
editor.commit();
}else{
editor.abort();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String hashKeyFormUrl(String url){
String cacheKey;
try {
final MessageDigest messageDigest=MessageDigest.getInstance("MD5");
messageDigest.update(url.getBytes());
cacheKey=bytesToHexString(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey=String.valueOf(url.hashCode());
}
return cacheKey;
}
private String bytesToHexString(byte[] bytes){
StringBuilder stringBuilder=new StringBuilder();
for(int i=0,length=bytes.length;i
DiskLruCache的缓存查找
缓存查找过程也需要将url转换为key,然后通过DiskLruCache的get方法得到一个Snapshot对象,接着再通过Snapshot对象即可得到缓存的文件输入流,有了输入流就可以得到Bitmap对象。
private void getDiskCache(String url){
Bitmap bitmap=null;
String key=hashKeyFormUrl(url);
try {
DiskLruCache.Snapshot snapshot=mDiskLruCache.get(key);
if(snapshot!=null){
FileInputStream fileInputStream= (FileInputStream) snapshot.getInputStream(0);
FileDescriptor fileDescriptor=fileInputStream.getFD();
bitmap=decodeSampleBitmapFromFileDescriptor(fileDescriptor,500,500);
if(bitmap!=null){
//显示图片或是将bitmap进行内存缓存
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static Bitmap decodeSampleBitmapFromFileDescriptor(FileDescriptor fileDescriptor,int reqWidth,int reqHeight){
final BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight);
options.inJustDecodeBounds=false;
return BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
}
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
int outWidth=options.outWidth;
int outHeight=options.outHeight;
int inSampleSize=1;
if(outHeight>reqHeight||outWidth>reqWidth){
final int halfHeight=outHeight/2;
final int halfWidth=outWidth/2;
while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
inSampleSize*=2;
}
}
return inSampleSize;
}
关于LruCache和DiskLruCache的内部实现会在后续文章中给出
(ps:希望大家能关注下我的公众号,毕竟写作不易,可以通过公众号和我交流一些技术,还有大家对Android有哪方面想知道的可以在底下评论,我会选些支持率高的内容进行讲解。
)