LruCache和DiskLruCache

前言

Android中的三级缓存主要就是内存缓存和硬盘缓存。

Lru(least recently used)意为最近最少使用算法,核心思想就是当缓存满时,会优先淘汰最近最少使用的缓存对象。

LruCache的使用

在Android中可以直接使用LruCache,算法原理是:把最近使用的对象存储在LinkedHashMap中,当缓存满时,把最近最少使用的对象从内存中移除,并提供了get和put方法类完成缓存的获取和添加操作。

int maxMemory = (int) (Runtime.getRuntime().totalMemory() / 1024);
// 缓存的大小,一般为当前进程可用容量的1/8
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
    // 重写sizeOf方法, 计算出要缓存的每张图片的大小
    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight() / 1024;
    }
};

利用LinkedHashMap的一个特性(accessOrder = true基于访问顺序)再加上对LinkedHashMap的数据操作上锁实现的缓存策略

  • 首先设置内部的LinkedHashMap构造参数accessOrder = true,实现了数据排序按照访问顺序
  • LruCache在调用get()方法是时,会调用LinkedHashMap的get()方法,会将此数据移到队尾
  • 最新访问的数据在尾部,如果要在存入数据,将移除队首最近最少访问的数据

再次总结一下原理:

  • LruCache中维护了一个LinkedHashMap,该LinkedHashMap是以访问顺序排序的
  • 当调用put()方法时,在结合处添加元素,并调用trimToSize()判断缓存是否已满,如果满了删除LinkedHashMap队首的元素
  • 当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队尾

具体分析不说了,贴一个我觉得写的好的链接:浅析LRUCache原理(Android)

DiskLruCache

// DiskLruCache是不能new出来的, 需要调用open()方法

// 缓存地址, app版本号, 一个Key可对应多少个文件, 最多可以缓存多少数据
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)


// SD卡存在
getExternalCahceDir()来获取到的是/sdcard/Android/data/<application package>/cache
// SD卡不存在
getCacheDir()获取到的是/data/data/<application package>/cache



// 标准open()写法
DiskLruCache mDiskLruCache = null;
try {
    File cacheDir = getDiskCacheDir(context, "bitmap);
    if (!cacheDir.exists()) {
        cacheDir.mkdirs();
    }
    mDiskLruCache = DiskLruCache.open(cacheDir, getApplication(context), 1, 10 * 1024 * 1024);
} catch (IOException e) {
    e.printStackTrace();
}




// 子线程中写入操作, 通过DiskLruCache.Editor
try {
	String imageUrl = "https://imgmy.csdn.net/uploads/201309/01/1378037235_7476.jpg";
	String key = hashKeyForDisk(imageUrl);
	DiskLruCache.Editor editor = mDiskLruCache.edit(key);
	if (editor != null) {
		OutputStream outputStream = editor.newOutputStream(0);
		if (downloadUrlToStream(imageUrl, outputStream)) {
			editor.commit();
		} else {
			editor.abort();
		}
	}
	mDiskLruCache.flush();
} catch (IOException e) {
	e.printStackTrace();
}



// 读取操作, 通过DiskLruCAche.Snapshot
try {
	String imageUrl = "https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg";
	String key = hashKeyForDisk(imageUrl);
	DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
	if (snapShot != null) {
		InputStream is = snapShot.getInputStream(0);
		Bitmap bitmap = BitmapFactory.decodeStream(is);
		mImage.setImageBitmap(bitmap);
	}
} catch (IOException e) {
	e.printStackTrace();
}


// 移除操作, 一般需要从网络重新获取最新数据的时候才应该调用
try {
	String imageUrl = "https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
	String key = hashKeyForDisk(imageUrl);  
	mDiskLruCache.remove(key);
} catch (IOException e) {
	e.printStackTrace();
}


// 其他API
    // 显示缓存数据大小
    size()
    // 将内存中的操作记录同步到日志文件中,不需要每次都调用
    flush()
    // 关闭,和open()对应的
    close()
    // 将所有的缓存数据清除
    delete()

解读journal

// 上面三个1依次是:DiskLruCache版本号,应用程序版本号,valueCount

dirty: 调用edit()方法时,会向journal文件中写入一条dirty记录, 表示正准备写入,但不知道是什么结果
clean: commit()方法表示写入缓存成功, 会向journal写入一条clean记录, 代表这条脏数据被“清洗”干净了 
remove:abort()方法表示写入缓存失败, 会向journal写入一条remove记录
每一行dirty的key, 后面都应该有一行对应的clean和remove的记录, 否则这条数据就是脏的, 会被自动清理掉

clean后面还会带有 此文件的字节数

除了这些还有read记录, 每当我们调用get()方法获取缓存时, 都会想journal文件中写入一条read记录

DiskLruCache使用了一个redundantOpCount变量来记录用户操作的次数, 当变量值达到2000就会重构journal的事件, 
保证journal文件的大小适中保持在一个合理的范围内

参考郭神的:Android DiskLruCache完全解析,硬盘缓存的最佳方案

当然还有一篇LruCache和DiskLruCache结合使用的实例:Android照片墙完整版,完美结合LruCache和DiskLruCache

总结

核心内容还是LruCache算法,通过利用LinkedHashMap,实现它最近最少使用的算法,访问过的元素加到队尾,缓存满了就去删除队首的元素,至于DiskLruCache,基本的用法是很简单的,不需要死记硬背。目前就总结这么多,有什么新的东西再总结吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值