一、概念
LruCache 是Android 3.1(API12)所提供的一个缓存类,缓存到达限制的时候优先删除近期最少使用。内部维护了一个 LinkedHashMap 以访问顺序排列,满了就删除尾元素(即近期最少使用的Least Recently Used),当获取缓存对象的同时会将该元素更新到队列头部。读写线程安全,对缓存的元素是强引用,不允许null键null值,为null说明没有缓存。
get() |
public final V get(K key) 获取缓存过的数据,不存在返回null。线程安全。 |
put() |
public final V put(K key, V value) 添加/更新缓存数据。线程安全。 |
remove() |
public final V remove(K key) 移除缓存的数据。 |
evictAll() |
public final void evictAll() 清除所有缓存数据 |
resize() |
public void resize(int maxSize) 重新设置最大缓存容量 |
trimToSize() |
public void trimToSize(int maxSize) 清除最老的缓存,直到剩余缓存数量满足指定大小 |
snapshot() |
public synchronized final Map<K, V> snapshot() 返回一个Map集合,元素按缓存访问次数最少到最多。 |
二、使用
-
设置 LruCache 缓存大小,一般为当前进程可用内存的1/8。
-
重写 sizeOf() 计算出要缓存的对象的大小(总容量和对象大小计算的单位要一直一致)。
private val lruCache by lazy {
//获取当前进程可用内存大小,并分配1/8用于缓存
val cacheSize = (Runtime.getRuntime().totalMemory() / 1024 / 8).toInt()
object : LruCache<String, Bitmap>(cacheSize) {
//必须重写此方法来测量每张图片的大小(单位要跟总缓存的单位一致)
//如果是缓存简单的对象,直接返回1
override fun sizeOf(key: String?, value: Bitmap?): Int {
//返回Bitmap大小(因为可以复用,换成更小的图也还是占用原来的大小)。
//而getByteCount()返回的是图片大小。
return value?.allocationByteCount ?: 0
}
}
}
//提供存入方法
fun putBitmap(key: String, value: Bitmap) {
lruCache.put(key, value)
}
//提供取出方法(返回null意味着不存在缓存)
fun getBitmap(key: String): Bitmap? {
return lruCache.get(key)
}
三、封装
3.1 工具类
object RamCache {
private val lruCache by lazy {
//获取当前进程可用内存大小,并分配1/8用于缓存
val cacheSize = (Runtime.getRuntime().totalMemory() / 1024 / 8).toInt()
object : LruCache<String, Pair<Long, Any>>(cacheSize) {
//必须重写此方法来测量每个对象的大小(单位要跟总缓存的单位一致)
override fun sizeOf(key: String, value: Pair<Long, Any>): Int {
return 1
}
}
}
//提供存入方法
fun put(key: String, value: Pair<Long, Any>) {
lruCache.put(key, value)
}
//提供取出方法(返回null意味着不存在缓存)
fun get(key: String): Pair<Long, Any>? {
return lruCache.get(key)
}
fun isExpored(key: String): Boolean {
val before = get(key)
return if (before == null) {
true
} else {
System.currentTimeMillis() - before.first >= 60000 //大于一分钟过期
}
}
}
3.2 函数封装
suspend fun <T> fetchData(
request: suspend () -> ApiResponse<T>
) = runCatching {
request()
}.then { apiResponse ->
apiResponse.getData()
}.onFailure {
Log.e("【错误】", it.message.toString())
}
//先判断RAM缓存是否过期(过期和不存在都返回false),未过期就使用RAM缓存
//过期就联网获取然后写入RAM缓存中
suspend fun <T> fetchDataWithRamCache(
cacheName: String,
request: suspend () -> ApiResponse<T>,
) = if (cacheName.isRamCacheExpired()) {
fetchData(request).onSuccess {
val value = Pair(System.currentTimeMillis(), it as Any)
RamCache.put(cacheName, value)
}
} else {
val obj = RamCache.get(cacheName)?.second as T
Result.success(obj)
}