一行代码搞定三级缓存
16lz
2021-01-23
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卡需要动态申请权限,自行百度
一、LruCache
不废话直接上工具类,初始化会在另一个管理缓存的工具类中使用,这里不需要操心
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(); }}
二、DisLruCache
今年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; }}
五、bitmap工具类
https://blog.csdn.net/a896159476/article/details/92996925
完整代码
https://github.com/a896159476/KotlinTest
更多相关文章
- android缓存框架ASimpleCache的使用 (网络请求数据并缓存)
- Android 定时器实现图片的变换
- android studio 小技巧之 图片预览
- android获取网络图片的用法 BitmapFactory.decodeByteArray
- Android 图片显示与屏幕适配的问题
- android获取手机上的图片和视频缩略图thumbnails
- android 引导用户指示操作 高亮显示 可以自定义文字或者图片来作
- Android 小項目之---Iphone拖动图片特效 (附源码)