Android的三级缓存,其中主要的就是内存缓存和硬盘缓存,分别是LruCache和DisLruCache。
LruCache是Android 3.1所提供的一个缓存类,所以在Android中可以直接使用LruCache实现内存缓存。而DisLruCache目前在Android 还不是Android SDK的一部分,但Android官方文档推荐使用该算法来实现硬盘缓存。
var listJob = ArrayList()
KotlinCacheUtil.INSTANCE.init(application)
val imageJob = KotlinCacheUtil.INSTANCE.setImageBitmap(
"https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg",
imageView,
R.drawable.ic_launcher
)
listJob.add(imageJob)
取消协程防止内存泄漏
override fun onDestroy() {
super.onDestroy()
for (job in listJob){
job.cancel()
}
}
本文使用了Kotlin协程需要导入kotlinx-coroutines,网络请求框架okhttp
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1'
implementation 'com.squareup.okhttp3:okhttp:3.14.1'
implementation 'com.squareup.okio:okio:2.2.2'
混淆代码
##okhttp3混淆
-keep class okhttp3.** {*;}
-dontwarn okhttp3.**
##okio混淆
-keep class okio.** {*;}
-dontwarn okio.**
6.0以上使用sd卡需要动态申请权限,自行百度
不废话直接上工具类,初始化会在另一个管理缓存的工具类中使用,这里不需要操心
public class LruCacheUtil {
private static volatile LruCacheUtil instance;
private LruCacheUtil() {}
public static LruCacheUtil getInstance() {
if (instance == null) {
synchronized (LruCacheUtil.class) {
if (instance == null) {
instance = new LruCacheUtil();
}
}
}
return instance;
}
private LruCache lruCache;
/**
* 初始化
*/
public void init() {
long maxMemory = Runtime.getRuntime().maxMemory();
int cacheSize = (int) (maxMemory / 8);
lruCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(@NotNull String key, @NotNull Bitmap value) {
return value.getByteCount();
}
};
}
/**
* 把Bitmap对象加入到缓存中
* @param imageUrl 图片url
* @param bitmap 加入缓存的图片
*/
public void addBitmapToMemory(String imageUrl, Bitmap bitmap) {
String key = Md5Util.getInstance().MD5(imageUrl);
if (getBitmapFromMemCache(key) == null) {
lruCache.put(key, bitmap);
}
}
/**
* 从缓存中得到Bitmap对象
* @param imageUrl 图片url
* @return Bitmap
*/
public Bitmap getBitmapFromMemCache(String imageUrl) {
String key = Md5Util.getInstance().MD5(imageUrl);
return lruCache.get(key);
}
/**
* 从缓存中删除指定的Bitmap
* @param imageUrl 图片url
*/
public void removeBitmapFromMemory(String imageUrl) {
String key = Md5Util.getInstance().MD5(imageUrl);
lruCache.remove(key);
}
/**
* 清除所有缓存
*/
public void evictAll(){
lruCache.evictAll();
}
}
今年Kotlin语言从安卓一等公民升级为第一等公民,是时候重新学习一波Kotlin了,DisLruCache工具类使用了新特性“协程”,没有了解的小伙伴可以先了解一下,用过后都说好;Kotlin的IO流值得吹一波,实在太好用了,inputStream.copyTo(outputStream)一行代码解决从一个流写到另一个流;最后吹一波Kotlin的单例模式,实在太好用了。
不要忘了这个
implementation 'com.jakewharton:disklrucache:2.0.2'
工具类
enum class DiskLruCacheUtil {
INSTANCE;
private lateinit var application: Application
private lateinit var diskLruCache: DiskLruCache
fun init(application: Application) {
this.application = application
try {
diskLruCache = DiskLruCache.open(directory("bitmap"), getAppVersion(), 1, (10 * 1024 * 1024).toLong())
} catch (e: IOException) {
e.printStackTrace()
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
}
/**
* 把Bitmap对象加入到缓存中
* @param imageUrl 图片url,如https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg
* @param bitmap 加入缓存的图片
*/
fun addBitmapToMemory(imageUrl: String){
try {
val key = Md5Util.getInstance().MD5(imageUrl)
val editor = diskLruCache.edit(key)
if (editor != null) {
val outputStream = editor.newOutputStream(0)
if (downloadUrlStream(imageUrl, outputStream)){
editor.commit()
}else{
editor.abort()
}
}
diskLruCache.flush()
} catch (e: IOException) {
e.printStackTrace()
}
}
/**
* 网络下载
* @param imageUrl 图片url
* @param outputStream diskLruCache的IO流
* @return 是否下载成功
*/
private fun downloadUrlStream(imageUrl: String, outputStream: OutputStream) :Boolean{
val request = Request.Builder()
.url(imageUrl)
.build()
try {
val response = HttpUtil.getInstance().client.newCall(request).execute()
if (response.isSuccessful) {
if (response.body()!= null){
val bis = response.body()!!.byteStream().buffered()
val bos = outputStream.buffered()
try {
bis.copyTo(bos)
return true
}catch (e: IOException) {
e.printStackTrace()
}finally {
bis.close()
bos.close()
}
}
}
} catch (e: IOException) {
e.printStackTrace()
}
return false
}
/**
* 从缓存中得到Bitmap对象
* @param imageUrl 图片url
* @return Bitmap
*/
fun getBitmapFromMemCache(imageUrl: String,maxWidth:Int,maxHeight:Int): Bitmap? {
val key = Md5Util.getInstance().MD5(imageUrl)
val snapShot = diskLruCache.get(key)
if (snapShot!=null){
val inputStream = snapShot.getInputStream(0)
return BitmapUtil.getInstance().getBitmap(inputStream,maxWidth,maxHeight,false)
}
return null
}
/**
* 从缓存中删除指定的Bitmap
* @param imageUrl 图片url
*/
fun removeBitmapFromMemory(imageUrl: String) {
val key = Md5Util.getInstance().MD5(imageUrl)
diskLruCache.remove(key)
}
/**
* 所有缓存数据的总字节数
*/
fun getSize():Long{
return diskLruCache.size()
}
/**
* 同步数据,在Activity的onPause()方法中去调用一次flush()方法
*/
fun flush(){
diskLruCache.flush()
}
/**
* 在Activity的onDestroy()方法中去调用close()方法
*/
fun close(){
diskLruCache.close()
}
/**
* 将所有的缓存数据全部删除
*/
fun delete(){
diskLruCache.delete()
}
/**
* 缓存目录
*/
private fun directory(fileName: String): File {
val path: String
//判断SD卡是否可用
if (Environment.MEDIA_MOUNTED == Environment.getExternalStorageState() || !Environment.isExternalStorageRemovable()) {
path = application.externalCacheDir!!.absolutePath
} else {
path = application.cacheDir.absolutePath
}
val file = File(path, fileName)
if (!file.exists()) {
file.mkdirs()
}
return file
}
/**
* app版本
*/
@Throws(PackageManager.NameNotFoundException::class)
private fun getAppVersion(): Int {
val info = application.packageManager.getPackageInfo(application.packageName, 0)
return info.versionCode
}
}
如果你有使用okhttp,那么也可以使用okhttp的DiskLruCache
enum class OKDiskLruCacheUtil {
INSTANCE;
private lateinit var application: Application
private lateinit var diskLruCache: DiskLruCache
fun init(application: Application) {
this.application = application
try {
diskLruCache = DiskLruCache.create(
FileSystem.SYSTEM,
directory("bitmap"),
getAppVersion(),
1,
10 * 1024 * 1024
)
} catch (e: IOException) {
e.printStackTrace()
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
}
/**
* 把Bitmap对象加入到缓存中
* @param imageUrl 图片url,如https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg
* @param bitmap 加入缓存的图片
*/
fun addBitmapToMemory(imageUrl: String){
try {
val key = Md5Util.getInstance().MD5(imageUrl)
val editor = diskLruCache.edit(key)
if (editor != null) {
val outputStream= editor.newSink(0).buffer().outputStream()
if (downloadUrlStream(imageUrl, outputStream)){
editor.commit()
}else{
editor.abort()
}
}
diskLruCache.flush()
} catch (e: IOException) {
e.printStackTrace()
}
}
/**
* 网络下载
* @param imageUrl 图片url
* @param outputStream diskLruCache的IO流
* @return 是否下载成功
*/
private fun downloadUrlStream(imageUrl: String, outputStream: OutputStream) :Boolean{
val request = Request.Builder()
.url(imageUrl)
.build()
try {
val response = HttpUtil.getInstance().client.newCall(request).execute()
if (response.isSuccessful) {
if (response.body()!= null){
val bis = response.body()!!.byteStream().buffered()
val bos = outputStream.buffered()
try {
bis.copyTo(bos)
return true
}catch (e: IOException) {
e.printStackTrace()
}finally {
bis.close()
bos.close()
}
}
}
} catch (e: IOException) {
e.printStackTrace()
}
return false
}
/**
* 从缓存中得到Bitmap对象
* @param imageUrl 图片url
* @return Bitmap
*/
fun getBitmapFromMemCache(imageUrl: String,maxWidth:Int,maxHeight:Int): Bitmap? {
val key = Md5Util.getInstance().MD5(imageUrl)
val snapShot = diskLruCache.get(key)
if (snapShot!=null){
val inputStream = snapShot.getSource(0).buffer().inputStream()
return BitmapUtil.getInstance().getBitmap(inputStream,maxWidth,maxHeight,false)
}
return null
}
/**
* 从缓存中删除指定的Bitmap
* @param imageUrl 图片url
*/
fun removeBitmapFromMemory(imageUrl: String) {
val key = Md5Util.getInstance().MD5(imageUrl)
diskLruCache.remove(key)
}
/**
* 所有缓存数据的总字节数
*/
fun getSize():Long{
return diskLruCache.size()
}
/**
* 同步数据,在Activity的onPause()方法中去调用一次flush()方法
*/
fun flush(){
diskLruCache.flush()
}
/**
* 在Activity的onDestroy()方法中去调用close()方法
*/
fun close(){
diskLruCache.close()
}
/**
* 将所有的缓存数据全部删除
*/
fun delete(){
diskLruCache.delete()
}
/**
* 缓存目录
*/
private fun directory(fileName: String): File {
val path: String
//判断SD卡是否可用
if (Environment.MEDIA_MOUNTED == Environment.getExternalStorageState() || !Environment.isExternalStorageRemovable()) {
path = application.externalCacheDir!!.absolutePath
} else {
path = application.cacheDir.absolutePath
}
val file = File(path, fileName)
if (!file.exists()) {
file.mkdirs()
}
return file
}
/**
* app版本
*/
@Throws(PackageManager.NameNotFoundException::class)
private fun getAppVersion(): Int {
val info = application.packageManager.getPackageInfo(application.packageName, 0)
return info.versionCode
}
}
enum class KotlinCacheUtil {
INSTANCE;
private lateinit var application: Application
/**
* 初始化
* @param application
*/
fun init(application: Application) {
this.application = application
screenWidth()
//内存缓存初始化
LruCacheUtil.getInstance().init()
//磁盘缓存初始化
OKDiskLruCacheUtil.INSTANCE.init(application)
//http初始化
HttpUtil.getInstance().init()
}
/**
* 三级缓存
* @param imageUrl 图片url
* @param imageView 显示控件
* @param placeholderBitmap 占位图
*/
private fun setImageBitmap(imageUrl: String, imageView: ImageView, placeholderBitmap: Bitmap) :Job{
//设置占位图
imageView.setImageBitmap(placeholderBitmap)
return GlobalScope.launch {
//1.先从内存读取
var bitmap: Bitmap? = LruCacheUtil.getInstance().getBitmapFromMemCache(imageUrl)
if (bitmap == null) {
//2.如果没有则从磁盘读取
bitmap = OKDiskLruCacheUtil.INSTANCE.getBitmapFromMemCache(imageUrl,width,height)
if (bitmap == null) {
if (isNetworkConnected()) {
//3.从网络获取并加入磁盘缓存
OKDiskLruCacheUtil.INSTANCE.addBitmapToMemory(imageUrl)
//从磁盘缓存中获取bitmap
bitmap = OKDiskLruCacheUtil.INSTANCE.getBitmapFromMemCache(imageUrl,width,height)
if (bitmap == null) {
launch (Dispatchers.Main){
show("没有可用网络")
}
return@launch
}
//加入内存缓存
LruCacheUtil.getInstance().addBitmapToMemory(imageUrl, bitmap)
} else {
//4.如果没有网络提示
launch (Dispatchers.Main){
show("没有可用网络")
}
return@launch
}
} else {
LruCacheUtil.getInstance().addBitmapToMemory(imageUrl, bitmap)
}
}
launch (Dispatchers.Main){
imageView.setImageBitmap(bitmap)
}
}
}
/**
* 三级缓存
* @param imageUrl 图片url
* @param imageView 显示控件
* @param resId 占位图的资源id
*/
fun setImageBitmap(imageUrl: String, imageView: ImageView, resId: Int):Job {
//设置占位图
val bitmap = BitmapUtil.getInstance().getBitmapResources(application.resources,resId,width,height,false)
return setImageBitmap(imageUrl,imageView,bitmap)
}
/**
* 三级缓存
* @param imageUrl 图片url
* @param imageView 显示控件
* @param file sd卡占位图
*/
fun setImageBitmap(imageUrl: String, imageView: ImageView, file: File):Job {
//设置占位图
val bitmap = BitmapUtil.getInstance().getBitmapFile(file,width,height,false)
return setImageBitmap(imageUrl,imageView,bitmap)
}
/**
* 三级缓存
* @param imageUrl 图片url
* @param imageView 显示控件
* @param path 占位图文件路径
* @param fileName 占位图文件名字
*/
fun setImageBitmap(imageUrl: String, imageView: ImageView, path:String , fileName:String):Job {
//设置占位图
val bitmap = BitmapUtil.getInstance().getBitmapFile(path,fileName,width,height,false)
return setImageBitmap(imageUrl,imageView,bitmap)
}
/**
* 清除所有缓存
*/
private fun delete() {
LruCacheUtil.getInstance().evictAll()
OKDiskLruCacheUtil.INSTANCE.delete()
}
/**
* 检测是否有网络连接
* @return 返回false无可用网络
*/
private fun isNetworkConnected(): Boolean {
val mConnectivityManager = application.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val mNetworkInfo = mConnectivityManager.activeNetworkInfo
if (mNetworkInfo != null) {
return mNetworkInfo.isConnected
}
return false
}
private fun show(msg: String) {
Toast.makeText(application, msg, Toast.LENGTH_LONG).show()
}
private var width = 0
private var height = 0
/**
* 获取屏幕宽高
*/
private fun screenWidth() {
val wm = application.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val display = wm.defaultDisplay
val size = Point()
display.getSize(size)
width = size.x
height = size.y
}
}
一个简单的小工具类,为懒癌患者准备
public class HttpUtil {
private static volatile HttpUtil instance;
private HttpUtil() {}
public static HttpUtil getInstance() {
if (instance == null) {
synchronized (HttpUtil.class) {
if (instance == null) {
instance = new HttpUtil();
}
}
}
return instance;
}
private OkHttpClient client;
public void init(){
client = new OkHttpClient.Builder()
//设置连接的连接超时的时间
.connectTimeout(10,TimeUnit.SECONDS)
//设置连接的读取超时时间
.readTimeout(30, TimeUnit.SECONDS)
//设置写入超时时间
.writeTimeout(10, TimeUnit.SECONDS)
.build();
}
public OkHttpClient getClient(){
return client;
}
}
https://blog.csdn.net/a896159476/article/details/92996925
https://github.com/a896159476/KotlinTest