Android小知识-Android中的缓存策略(内存缓存和磁盘缓存)

本篇文章已授权微信公众号 顾林海 独家发布

通过缓存策略,我们不需要每次都从网上请求图片或从存储设备中加载图片,这样就极大地提高了图片的加载效率以及用户体验。目前比较常用的缓存策略是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有哪方面想知道的可以在底下评论,我会选些支持率高的内容进行讲解。


Android小知识-Android中的缓存策略(内存缓存和磁盘缓存)_第1张图片
838794-506ddad529df4cd4.webp.jpg

你可能感兴趣的:(Android小知识-Android中的缓存策略(内存缓存和磁盘缓存))