Android缓存机制-LRU cache原理与用法
在使用Android图片加载框架时,经常会提到三级缓存,其中主要的是内存缓存和文件缓存。 两个缓存都是用到了LruCache算法,在Android分别对应:LruCache和DiskLruCache。
- IRU算法
操作系统中进行内存管理中时采用一些页面置换算法,如LRU、LFU和FIFO等。
其中LRU(Least recently used,最近最少使用)算法,核心思想是当缓存达到上限时,会先淘汰最近最少使用的缓存。这样可以保证缓存处于一种可控的状态,有效的防止OOM的出现。
LruCache用法
LruCache是从Android3.1开始支持,目前已经在androidx.collection支持。
初始化
LruCache初始化:
val maxCache = (Runtime.getRuntime().maxMemory() / 1024).toInt()
//初始化大小:内存的1/8
val cacheSize = maxCache / 8
var memorySize = object : LruCache<String, Bitmap>(512) {
override fun sizeOf(key: String, value: Bitmap): Int {
//重写该方法,计算每张要缓存的图片大小
return value.byteCount / 1024
}
}
原文
一、 我们建立一个简单的项目去体会LruCache的使用过程
通过http请求网络上的图片文件,然后保存在缓存中。显示图片时,先从缓存中取,如果没有,就发送请求向服务器取。项目结构如下:
二、 在AndroidManifest.xml文件中,加入网络权限的声明:
<uses-permission android:name="android.permission.INTERNET"/>
三、 使用实例
val maxCache = (Runtime.getRuntime().maxMemory() / 1024).toInt()
//初始化大小:内存的1/8
val cacheSize = maxCache / 8
var lruCache = object : LruCache<String, Bitmap>(cacheSize) {
override fun sizeOf(key: String, value: Bitmap): Int {
//重写该方法,计算每张要缓存的图片大小
return value.byteCount / 1024
}
}
//获取图片
fun HandleRequest(target: T) {
try {
val url = mRequestMap.get(target)
if (url == null) {
return
}
var bitmap: Bitmap? = null
if (lruCache.get(url) === null) { // 如果缓存中没有就重新获取
var bitmapBytes: ByteArray = FlickrFetchr().getUrlBytes(url)
bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.size)
lruCache.put(url, bitmap)
} else {
bitmap = lruCache.get(url) //否则取出
}
Log.i(TAG, "bitmap created")
mResponseHandler.post(object : Runnable { // 应为MResponseHanlder与主线程相关联,所以UI更新代码会在主程序中完成
override fun run() {
if (mRequestMap.get(target) != url || mHasQuit) { // 检查target是否改变,和当前主线程是否销毁
return
}
mRequestMap.remove(target)//删除已完成下载的target
bitmap?.let { mThumbnailDownloadListener?.onThumbnailDownload(target, it) } // 发送bitmap给主线程
}
})
} catch (ioe: IOException) {
Log.e(TAG, "Error downloading image", ioe)
}
}
完整类
package com.example.apps.photogallery
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Handler
import android.os.HandlerThread
import android.os.Message
import android.util.Log
import androidx.collection.LruCache
import java.io.IOException
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
class ThumbnailDownload<T> : HandlerThread {
private var mHasQuit = false
//用来存储对Handler的引用,这个Handler负责在ThumbnailDownload后台线程上管理下载请求消息队列
private var mRequestHandler: Handler? = null
private var mRequestMap: ConcurrentMap<T, String> = ConcurrentHashMap()//线程安全的map
private var mResponseHandler: Handler
private var mThumbnailDownloadListener: ThumbnailDownloadListener<T>? = null
val maxCache = (Runtime.getRuntime().maxMemory() / 1024).toInt()
//初始化大小:内存的1/8
val cacheSize = maxCache / 8
var lruCache = object : LruCache<String, Bitmap>(cacheSize) {
override fun sizeOf(key: String, value: Bitmap): Int {
//重写该方法,计算每张要缓存的图片大小
return value.byteCount / 1024
}
}
//获取图片
fun HandleRequest(target: T) {
try {
val url = mRequestMap.get(target)
if (url == null) {
return
}
var bitmap: Bitmap? = null
if (lruCache.get(url) === null) { // 如果缓存中没有就重新获取
var bitmapBytes: ByteArray = FlickrFetchr().getUrlBytes(url)
bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.size)
lruCache.put(url, bitmap)
} else {
bitmap = lruCache.get(url) //否则取出
}
Log.i(TAG, "bitmap created")
mResponseHandler.post(object : Runnable { // 应为MResponseHanlder与主线程相关联,所以UI更新代码会在主程序中完成
override fun run() {
if (mRequestMap.get(target) != url || mHasQuit) { // 检查target是否改变,和当前主线程是否销毁
return
}
mRequestMap.remove(target)//删除已完成下载的target
bitmap?.let { mThumbnailDownloadListener?.onThumbnailDownload(target, it) } // 发送bitmap给主线程
}
})
} catch (ioe: IOException) {
Log.e(TAG, "Error downloading image", ioe)
}
}
constructor(response: Handler) : super(TAG) {
this.mResponseHandler = response
}
public interface ThumbnailDownloadListener<T> {
fun onThumbnailDownload(target: T, thumbnai: Bitmap);
}
public fun setThumbnailDownloadListener(listener: ThumbnailDownloadListener<T>) {
mThumbnailDownloadListener = listener
}
override fun quit(): Boolean {
mHasQuit = true
return super.quit()
}
// onLooperPrepared在Looper首次检查消息队列之前调用,所以该方法是实现Handler的好地方
override fun onLooperPrepared() {
mRequestHandler = object : Handler() {
override fun handleMessage(msg: Message) {
if (msg.what == MESSAGE_DOWNLOAD) {
var target = msg.obj as T
Log.i(TAG, "Got a request for url: " + mRequestMap.get(target))
HandleRequest(target) //处理消息
}
}
}
}
fun clearQueue() {
mRequestHandler?.removeMessages(MESSAGE_DOWNLOAD)
mRequestMap.clear()
}
fun queueThumbnail(target: T, url: String) {
Log.i(TAG, "Got a URL: " + url)
if (url === null) {
mRequestMap.remove(target)
} else {
mRequestMap.put(target, url);
mRequestHandler?.obtainMessage(MESSAGE_DOWNLOAD, target)
?.sendToTarget()//obtainMessage方法会从公共回收池里获取消息
}
}
companion object {
val TAG = "ThumbnailDownload"
val MESSAGE_DOWNLOAD = 0 //用来标识下载请求消息,ThumbnailDownload会把它设为任何新创建消息的WHAT属性
}
}