Android下的缓存策略

Android下的缓存策略

内存缓存

经常使用的内存缓存是软引用和弱引用,大部分的使用方式是Android提供的LRUCache缓存策略,本质是个LinkedHashMap(会根据使用次数进行排序)java

磁盘缓存

DiskLruCache:非谷歌官方编写,可是得到官方认证android

  • 不限制数据缓存的位置,可自由的设置,一般状况下会选择:/sdcard/Android/data/{packageName}/cache这个路径
选择这个位置的好处
  1. 这是存储在sdcard上的,只要sdcard空间足够,不会对手机内置存储有任何影响
  2. 该路径被Android系统认定为应用程序的缓存路径,当app被卸载时,这里的数据会被一块儿清除掉,而不会出现删除app后,还残留数据的问题

getCacheDir()是获取app在手机内部存储的cache目录
getFilesDir()是获取app在手机内部存储的files目录算法

经过Context.getExternalFilesDir()能够获取到app在sdcard上的files目录,一般用于存放要长时间保存的数据
经过Context.getExternalCacheDir()能够获取到app在sdcard上的cache目录,一般用于存放一些临时数据
使用上面两个api,在app被卸载的时候,在sdcard上对应的全部文件也会自动被删除,不会留下垃圾信息
并且上面两个目录在设置里的应用详情里,可使用清除数据和清除缓存来清理临时文件api

LruCache——内存缓存策略

LRU(Least Recently Used)缓存算法,近期最少使用的算法
LruCache是Android 3.1之后提供的一个缓存类数组

LruCache的介绍

  • LruCache是 一个泛型类,主要原理是把最近使用的对象用强引用的方式存储在LinkedHashMap中,把最近最少使用的对象从内存中移除,并提供get和put方法来完成缓存的获取和添加操做

LruCache的缓存大小通常为当前进程可用容量的1/8
重写sizeOf方法,计算每一个缓存对象的大小
注意:缓存的总容量和每一个缓存对象的大小所用的单位要一致缓存

LruCahe的实现原理

  • 维护一个缓存对象列表,按照访问顺序进行排序
  • 一直没有访问的对象放在队尾,即将被淘汰
  • 最近访问的对象放在队首,最后被淘汰
  • 这个队列由LinkedHashMap来维护

LinkedHashMap

  • 是由数组+双向链表的数据结构来实现
  • 双向链表的结构能够实现访问顺序和插入顺序,使得队列中的对象按照必定的顺序排列起来

在构造函数中,可使用accessOrder参数来控制双向链表的结构是访问顺序仍是插入顺序
其中accessOrder设置为true则为访问顺序,为false,则为插入顺序。
设置LinkedHashMap的accessOrder为true,并向里面插入数据后随机访问数据,将访问数据后的LinkedHashMap输出,最近访问数据的最后输出网络

LruCache的源码分析

LruCache内部使用LinkedHashMap的访问顺序特性,来缓存数据
当调用put()方法时,就会在集合中添加元素,并调用trimToSize()来判断缓存是否已满,若是满了就删除队尾元素
当调用get()方法时,就会调用LinkedHashMap的get()方法得到对应的元素,同时会更新该元素到队首数据结构

DiskLruCache——磁盘缓存策略

DiskLruCache目前还不是Android SDK的一部分,可是Android官方文档推荐使用该算法来实现磁盘缓存app

DiskLruCache的使用方法

打开

DiskLruCache不能new出实例,须要调用它的open()方法
open()方法接收四个参数:函数

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
  • directory:数据的缓存地址
  • appVersion:当前应用程序的版本号
  • valueCount:同一个key能够对应多少个缓存文件,基本都是传1
  • maxSize:最多能够缓存多少字节的数据
  • 其中缓存地址前面已经说过了,一般都会存放在 /sdcard/Android/data/ /cache 这个路径下面,但同时咱们又须要考虑若是这个手机没有SD卡,或者SD正好被移除了的状况,所以比较优秀的程序都会专门写一个方法来获取缓存地址
public File getDiskCacheDir(Context context, String uniqueName) {  
    String cachePath;  
    if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())  
            || !Environment.isExternalStorageRemovable()) {  
        cachePath = context.getExternalCacheDir().getPath();  
    } else {  
        cachePath = context.getCacheDir().getPath();  
    }  
    return new File(cachePath + File.separator + uniqueName);  
}
  • 能够看到,当SD卡存在或者SD卡不可被移除的时候,就调用getExternalCacheDir()方法来获取缓存路径,不然就调用getCacheDir()方法来获取缓存路径。前者获取到的就是 /sdcard/Android/data/ /cache 这个路径,然后者获取到的是 /data/data/ /cache 这个路径。

  • 接着是应用程序版本号,咱们可使用以下代码简单地获取到当前应用程序的版本号:

public int getAppVersion(Context context) {  
    try {  
        PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);  
        return info.versionCode;  
    } catch (NameNotFoundException e) {  
        e.printStackTrace();  
    }  
    return 1;  
}
  • 须要注意的是,每当版本号改变,缓存路径下存储的全部数据都会被清除掉,由于DiskLruCache认为当应用程序有版本更新的时候,全部的数据都应该从网上从新获取。
DiskLruCache mDiskLruCache = null;  
try {  
    File cacheDir = getDiskCacheDir(context, "bitmap");  
    if (!cacheDir.exists()) {  
        cacheDir.mkdirs();  
    }  
    mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);  
} catch (IOException e) {  
    e.printStackTrace();  
}
插入缓存

插入使用DiskLruCache.Editor这个类来完成,一样,它也不能new出实例,须要使用edit()方法

public Editor edit(String key) throws IOException
  • 能够看到,edit()方法接收一个参数key,这个key将会成为缓存文件的文件名,而且必需要和图片的URL是一一对应的。那么怎样才能让key和图片的URL可以一一对应呢?直接使用URL来做为key?不太合适,由于图片URL中可能包含一些特殊字符,这些字符有可能在命名文件时是不合法的。其实最简单的作法就是将图片的URL进行MD5编码,编码后的字符串确定是惟一的,而且只会包含0-F这样的字符,彻底符合文件的命名规则。
读取缓存
// get()方法要求传入一个key来获取到相应的缓存数据,而这个key毫无疑问就是将图片URL进行MD5编码后的值了
public synchronized Snapshot get(String key) throws IOException
  • 经过get()方法,会获取到一个DiskLruCache.Snapshot对象,经过调用它的getInputStream()方法就能够获得缓存文件的输入流
移除缓存
public synchronized boolean remove(String key) throws IOException
  • 这个方法咱们并不该该常常去调用它。由于你彻底不须要担忧缓存的数据过多从而占用SD卡太多空间的问题,DiskLruCache会根据咱们在调用open()方法时设定的缓存最大值来自动删除多余的缓存。只有你肯定某个key对应的缓存内容已通过期,须要从网络获取最新数据的时候才应该调用remove()方法来移除缓存。
其余api
  • size():返回当前缓存路径下全部缓存数据的总字节数数,用于在app上显示当前缓存的总大小
  • flush():用于将内存中的操做同步到日志文件中(也就是journal文件),所以DiskLruCache可以正常工做的前提是要依赖于journal文件中的内容。频繁的调用会增长同步journal文件的时间,比较标准的作法就是爱Activity的onPause()方法中调用一次便可
  • close():用于将DiskLruCache关闭,和open()方法相对应。关闭掉了以后就不能再调用DiskLruCache中任何操做缓存数据的方法,一般只应该在Activity的onDestroy()方法中去调用close()方法。
  • delete():这个方法用于将全部的缓存数据所有删除,用于让用户清除缓存

参考文档:

http://blog.csdn.net/guolin_blog/article/details/28863651
http://blog.csdn.net/guolin_blog/article/details/34093441

相关文章
相关标签/搜索