Android图片缓存之Lru算法
前言:
上篇我们总结了Bitmap的处理,同时对比了各种处理的效率以及对内存占用大小。我们得知一个应用如果使用大量图片就会导致OOM(out of memory),那该如何处理才能近可能的降低oom发生的概率呢?之前我们一直在使用SoftReference软引用,SoftReference是一种现在已经不再推荐使用的方式,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用变得不再可靠,所以今天我们来认识一种新的缓存处理算法Lru,然后学习一下基于Lru的Lrucache、DiskLruCache 实现我们的图片缓存。
图片缓存相关博客地址:
- Android图片缓存之Bitmap详解
- Android图片缓存之初识Glide
- Android图片缓存之Glide进阶
- Android图片缓存之Lru算法
Lru:
LRU是Least Recently Used 的缩写,翻译过来就是“最近最少使用”,LRU缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉,比如我们缓存10000条数据,当数据小于10000时可以随意添加,当超过10000时就需要把新的数据添加进来,同时要把过期数据删除,以确保我们最大缓存10000条,那怎么确定删除哪条过期数据呢,采用LRU算法实现的话就是将最老的数据删掉。
基于LruCache实现内存缓存:
1.)初始化MemoryCache
这里内存缓存的是Drawable 而不是Bitmap 理由是Drawable相对Bitmap来说有很大的内存优势
int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取系统分配给应用的总内存大小int mCacheSize = maxMemory / 8;//设置图片内存缓存占用八分之一mMemoryCache = new LruCache<String, Drawable>(mCacheSize) {//必须重写此方法,来测量Bitmap的大小 @Overrideprotected int sizeOf(String key, Drawable value) {if (value instanceof BitmapDrawable) {Bitmap bitmap = ((BitmapDrawable) value).getBitmap();return bitmap == null ? 0 : bitmap.getByteCount();}return super.sizeOf(key, value);}};
2.)添加一个Drawable到内存缓存
/*** 添加Drawable到内存缓存** @param key* @param drawable*/private void addDrawableToMemoryCache(String key, Drawable drawable) {if (getDrawableFromMemCache(key) == null && drawable != null) {mMemoryCache.put(key, drawable);}}
3.)从内存缓存中获取一个Drawable
/*** 从内存缓存中获取一个Drawable** @param key* @return*/public Drawable getDrawableFromMemCache(String key) {return mMemoryCache.get(key);}
4.)从内存缓存中移除一个Drawable
/*** 从内存缓存中移除** @param key*/public void removeCacheFromMemory(String key) {mMemoryCache.remove(key);}
5.)清空内存缓存
/*** 清理内存缓存*/public void cleanMemoryCCache() {mMemoryCache.evictAll();}
其实Lru缓存机制本质上就是存储在一个LinkedHashMap存储,为了保障插入的数据顺序,方便清理。
基于DiskLruCache实现磁盘缓存:
DiskLruCache类并不是谷歌官方实现,需要自行下载,下载地址:https://github.com/JakeWharton/DiskLruCache
1.)初始化DiskLruCache
File cacheDir = context.getCacheDir();//指定的是数据的缓存地址long diskCacheSize = 1024 * 1024 * 30;//最多可以缓存多少字节的数据int appVersion = DiskLruUtils.getAppVersion(context);//指定当前应用程序的版本号int valueCount = 1;//指定同一个key可以对应多少个缓存文件try {mDiskCache = DiskLruCache.open(cacheDir, appVersion, valueCount, diskCacheSize);} catch (Exception ex) {}
2.)写入一个文件到磁盘缓存
/*** 添加Bitmap到磁盘缓存** @param key* @param value*/private void addBitmapToDiskCache(String key, byte[] value) {OutputStream out = null;try {DiskLruCache.Editor editor = mDiskCache.edit(key);if (editor != null) {out = editor.newOutputStream(0);if (value != null && value.length > 0) {out.write(value);out.flush();editor.commit();} else {editor.abort();}}mDiskCache.flush();} catch (IOException e) {e.printStackTrace();} finally {DiskLruUtils.closeQuietly(out);}}
3.)从磁盘缓存中读取Drawable
/*** 从磁盘缓存中获取一个Drawable** @param key* @return*/public Drawable getDrawableFromDiskCache(String key) {try {DiskLruCache.Snapshot snapShot = mDiskCache.get(key);if (snapShot != null) {InputStream is = snapShot.getInputStream(0);Bitmap bitmap = BitmapFactory.decodeStream(is);Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);//从磁盘中读取到之后 加入内存缓存 addDrawableToMemoryCache(key, drawable);return drawable;}} catch (IOException e) {e.printStackTrace();}return null;}
4.)从磁盘缓存中移除
/*** 从磁盘缓存中移除** @param key*/public void removeCacheFromDisk(String key) {try {mDiskCache.remove(key);} catch (Exception e) {}}
5.)清空磁盘缓存
/*** 清理磁盘缓存*/public void cleanDiskCache() {try {mDiskCache.delete();} catch (Exception e) {}}
图片下载过程:
接下来实例中用到了一点RxJava的知识有不了解RxJava的请自行了解一下。
1.)采用异步方式操作磁盘缓存和网络下载, 内存缓存可以在主线程中操作
public void disPlay(final ImageView imageView, String imageUrl) {//生成唯一keyfinal String key = DiskLruUtils.hashKeyForDisk(imageUrl);//先从内存中读取Drawable drawableFromMemCache = getDrawableFromMemCache(key);if (drawableFromMemCache != null) {imageView.setImageDrawable(drawableFromMemCache);return;}Observable.just(imageUrl).map(new Func1<String, Drawable>() {@Overridepublic Drawable call(String imageUrl) { // 参数类型 String//从磁盘中读取Drawable drawableFromDiskCache = getDrawableFromDiskCache(key);if (drawableFromDiskCache != null) {return drawableFromDiskCache;}//网络下载return download(imageUrl); // 返回类型 Drawable }}).subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程.subscribe(new Action1<Drawable>() {@Overridepublic void call(Drawable drawable) { // 参数类型 Drawable imageView.setImageDrawable(drawable);}});}
2.)下载图片过程以及处理
private Drawable download(String imageUrl) {HttpURLConnection urlConnection = null;ByteArrayOutputStream bos = null;InputStream ins = null;try {final URL url = new URL(imageUrl);urlConnection = (HttpURLConnection) url.openConnection();ins = urlConnection.getInputStream();bos = new ByteArrayOutputStream();int b;while ((b = ins.read()) != -1) {bos.write(b);}bos.flush();byte[] bytes = bos.toByteArray();Bitmap bitmap = DiskLruUtils.bytes2Bitmap(bytes);String key = DiskLruUtils.hashKeyForDisk(imageUrl);Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);//加入内存缓存 addDrawableToMemoryCache(key, drawable);//加入磁盘缓存 addBitmapToDiskCache(key, bytes);return drawable;} catch (IOException e) {e.printStackTrace();} finally {if (urlConnection != null) {urlConnection.disconnect();}DiskLruUtils.closeQuietly(bos);DiskLruUtils.closeQuietly(ins);}return null;}
附上最终图片缓存单例简单实现全部代码以及DiskLruUtils工具类代码
ImageLoadManager.java
public class ImageLoadManager {private LruCache<String, Drawable> mMemoryCache;//内存缓存private DiskLruCache mDiskCache;//磁盘缓存private static ImageLoadManager mInstance;//获取图片下载单例引用/*** 构造器** @param context*/private ImageLoadManager(Context context) {int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取系统分配给应用的总内存大小int mCacheSize = maxMemory / 8;//设置图片内存缓存占用八分之一mMemoryCache = new LruCache<String, Drawable>(mCacheSize) {//必须重写此方法,来测量Bitmap的大小 @Overrideprotected int sizeOf(String key, Drawable value) {if (value instanceof BitmapDrawable) {Bitmap bitmap = ((BitmapDrawable) value).getBitmap();return bitmap == null ? 0 : bitmap.getByteCount();}return super.sizeOf(key, value);}};File cacheDir = context.getCacheDir();//指定的是数据的缓存地址long diskCacheSize = 1024 * 1024 * 30;//最多可以缓存多少字节的数据int appVersion = DiskLruUtils.getAppVersion(context);//指定当前应用程序的版本号int valueCount = 1;//指定同一个key可以对应多少个缓存文件try {mDiskCache = DiskLruCache.open(cacheDir, appVersion, valueCount, diskCacheSize);} catch (Exception ex) {}}/*** 获取单例引用** @return*/public static ImageLoadManager getInstance(Context context) {ImageLoadManager inst = mInstance;if (inst == null) {synchronized (RequestManager.class) {inst = mInstance;if (inst == null) {inst = new ImageLoadManager(context.getApplicationContext());mInstance = inst;}}}return inst;}public void disPlay(final ImageView imageView, String imageUrl) {//生成唯一keyfinal String key = DiskLruUtils.hashKeyForDisk(imageUrl);//先从内存中读取Drawable drawableFromMemCache = getDrawableFromMemCache(key);if (drawableFromMemCache != null) {imageView.setImageDrawable(drawableFromMemCache);return;}Observable.just(imageUrl).map(new Func1<String, Drawable>() {@Overridepublic Drawable call(String imageUrl) { // 参数类型 String//从磁盘中读取Drawable drawableFromDiskCache = getDrawableFromDiskCache(key);if (drawableFromDiskCache != null) {return drawableFromDiskCache;}//网络下载return download(imageUrl); // 返回类型 Drawable }}).subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程.subscribe(new Action1<Drawable>() {@Overridepublic void call(Drawable drawable) { // 参数类型 Drawable imageView.setImageDrawable(drawable);}});}/*** 添加Drawable到内存缓存** @param key* @param drawable*/private void addDrawableToMemoryCache(String key, Drawable drawable) {if (getDrawableFromMemCache(key) == null && drawable != null) {mMemoryCache.put(key, drawable);}}/*** 从内存缓存中获取一个Drawable** @param key* @return*/public Drawable getDrawableFromMemCache(String key) {return mMemoryCache.get(key);}/*** 从磁盘缓存中获取一个Drawable** @param key* @return*/public Drawable getDrawableFromDiskCache(String key) {try {DiskLruCache.Snapshot snapShot = mDiskCache.get(key);if (snapShot != null) {InputStream is = snapShot.getInputStream(0);Bitmap bitmap = BitmapFactory.decodeStream(is);Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);//从磁盘中读取到之后 加入内存缓存 addDrawableToMemoryCache(key, drawable);return drawable;}} catch (IOException e) {e.printStackTrace();}return null;}/*** 添加Bitmap到磁盘缓存** @param key* @param value*/private void addBitmapToDiskCache(String key, byte[] value) {OutputStream out = null;try {DiskLruCache.Editor editor = mDiskCache.edit(key);if (editor != null) {out = editor.newOutputStream(0);if (value != null && value.length > 0) {out.write(value);out.flush();editor.commit();} else {editor.abort();}}mDiskCache.flush();} catch (IOException e) {e.printStackTrace();} finally {DiskLruUtils.closeQuietly(out);}}private Drawable download(String imageUrl) {HttpURLConnection urlConnection = null;ByteArrayOutputStream bos = null;InputStream ins = null;try {final URL url = new URL(imageUrl);urlConnection = (HttpURLConnection) url.openConnection();ins = urlConnection.getInputStream();bos = new ByteArrayOutputStream();int b;while ((b = ins.read()) != -1) {bos.write(b);}bos.flush();byte[] bytes = bos.toByteArray();Bitmap bitmap = DiskLruUtils.bytes2Bitmap(bytes);String key = DiskLruUtils.hashKeyForDisk(imageUrl);Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);//加入内存缓存// addDrawableToMemoryCache(key, drawable);//加入磁盘缓存 addBitmapToDiskCache(key, bytes);return drawable;} catch (IOException e) {e.printStackTrace();} finally {if (urlConnection != null) {urlConnection.disconnect();}DiskLruUtils.closeQuietly(bos);DiskLruUtils.closeQuietly(ins);}return null;}/*** 从缓存中移除** @param key*/public void removeCache(String key) {removeCacheFromMemory(key);removeCacheFromDisk(key);}/*** 从内存缓存中移除** @param key*/public void removeCacheFromMemory(String key) {mMemoryCache.remove(key);}/*** 从磁盘缓存中移除** @param key*/public void removeCacheFromDisk(String key) {try {mDiskCache.remove(key);} catch (Exception e) {}}/*** 磁盘缓存大小** @return*/public long diskCacheSize() {return mDiskCache.size();}/*** 内存缓存大小** @return*/public long memoryCacheSize() {return mMemoryCache.size();}/*** 关闭磁盘缓存*/public void closeDiskCache() {try {mDiskCache.close();} catch (Exception e) {}}/*** 清理缓存*/public void cleanCache() {cleanMemoryCCache();cleanDiskCache();}/*** 清理磁盘缓存*/public void cleanDiskCache() {try {mDiskCache.delete();} catch (Exception e) {}}/*** 清理内存缓存*/public void cleanMemoryCCache() {mMemoryCache.evictAll();} }
ImageLoadManager.java
DiskLruUtils.java
final class DiskLruUtils {/*** 关闭输入输出流*/public static void closeQuietly(/*Auto*/Closeable closeable) {if (closeable != null) {try {closeable.close();} catch (RuntimeException rethrown) {throw rethrown;} catch (Exception ignored) {}}}/*** 获取versionCode*/public static int getAppVersion(Context context) {try {PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);return info.versionCode;} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();}return 1;}public static String hashKeyForDisk(String key) {String cacheKey;try {final MessageDigest mDigest = MessageDigest.getInstance("MD5");mDigest.update(key.getBytes());cacheKey = bytesToHexString(mDigest.digest());} catch (NoSuchAlgorithmException e) {cacheKey = String.valueOf(key.hashCode());}return cacheKey;}public static String bytesToHexString(byte[] bytes) {StringBuilder sb = new StringBuilder();for (int i = 0; i < bytes.length; i++) {String hex = Integer.toHexString(0xFF & bytes[i]);if (hex.length() == 1) {sb.append('0');}sb.append(hex);}return sb.toString();}/*** Bitmap → bytes*/public static byte[] bitmap2Bytes(Bitmap bm) {if (bm == null) {return null;}ByteArrayOutputStream baos = new ByteArrayOutputStream();bm.compress(Bitmap.CompressFormat.PNG, 100, baos);return baos.toByteArray();}/*** bytes → Bitmap*/public static Bitmap bytes2Bitmap(byte[] bytes) {return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);}/*** Drawable → Bitmap*/public static Bitmap drawable2Bitmap(Drawable drawable) {if (drawable == null) {return null;}// 取 drawable 的长宽int w = drawable.getIntrinsicWidth();int h = drawable.getIntrinsicHeight();// 取 drawable 的颜色格式Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;// 建立对应 bitmapBitmap bitmap = Bitmap.createBitmap(w, h, config);// 建立对应 bitmap 的画布Canvas canvas = new Canvas(bitmap);drawable.setBounds(0, 0, w, h);// 把 drawable 内容画到画布中 drawable.draw(canvas);return bitmap;}/** Bitmap → Drawable*/public static Drawable bitmap2Drawable(Bitmap bm) {if (bm == null) {return null;}BitmapDrawable bd = new BitmapDrawable(bm);bd.setTargetDensity(bm.getDensity());return new BitmapDrawable(bm);}}
DiskLruUtils.java
总结:
以上就是基于Lru图片缓存简单实现
Android图片缓存之Lru算法相关推荐
- android图片缓存,直接应用项目中的Android图片缓存技术
前不久搞的Android图片缓存,刚开始引入开源的框架,用着还行,但是在开发中遇到问题,就比如universal-image-loader-1.9.5.jar这个框架吧,在加载图片的时候自定义imag ...
- Android 图片缓存之内存缓存技术LruCache,软引用
Android 图片缓存之内存缓存技术LruCache,软引用
- Android图片缓存框架Glide
Android图片缓存框架Glide Glide是Google提供的一个组件.它具有获取.解码和展示视频剧照.图片.动画等功能.它提供了灵活的API,帮助开发者将Glide应用在几乎任何网络协议栈中. ...
- Android 图片缓存机制
1.采用线程池 2.内存缓存+文件缓存 3.内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的1/4 4.对下载的图片进行按比例缩放,以减少内存 ...
- Android图片缓存管理
在一个app中,图片资源是处处存在的,加载图片的流程一般是: 1 先从缓存中读取 2 若缓存中不存在,从SD卡中读取 3 若SD卡中也不存在,则从服务器拉取 后两个步骤纯碎属于业务逻辑,暂且不表,这里 ...
- android 图片缓存工具类,Android工具类系列-Glide图片缓存与圆角
Glide的图片缓存和清除图片缓存 public class GlideCacheUtil { private static GlideCacheUtil inst; public static Gl ...
- android 图片缓存
一.Picasso https://github.com/square/picasso Picasso是Square公司开源的一个Android平台上的图片加载框架,简单易用,一句话搞定项目中的图片加 ...
- Android图片缓存框架 - Fresco的GenericDraweeHierarchy (五)
目录 1.Fresco 简介 2. Fresco 文档 3. Fresco开发步骤 4. Fresco加载图片6种方式 5. Drawees xm属性设置 6 Fresco实现圆角或圆形图片 7. D ...
- Android图片缓存框架 - Fresco实现圆角或圆形图片 (四)
目录 1.Fresco 简介 2. Fresco 文档 3. Fresco开发步骤 4. Fresco加载图片6种方式 5. Drawees xm属性设置 6 Fresco实现圆角或圆形图片 6.1 ...
最新文章
- eclipse常用快捷键Get;set;
- [Java] 02 String的常用方法
- React创建组件的三种方式及其区别
- Subversion基本操作
- Mysql 基本框架 select的全过程
- oracle 批量修改表结构,关于Oracle批量修改表结构相关内容的整理
- STM32(五)------GPIO位带操作
- Linux下怎么使用任务管理器和真人接口源码出售进程管理
- 零基础Python知识点回顾(三)
- [25年后的统计系会是什么样?
- C/c++输入输出函数
- labview霍夫曼编码_毕业设计 基于LabVIEW的编码的设计与仿真—信源编码
- iOS 基础入门--Bull' Eye 小游戏
- Kubesphere-多节点安装
- static(静态变量,方法)
- PHP中一些通用和易混淆技术点的最佳编程实践
- LiteOS 学习第一篇
- java生成1到10的随机数_用java生成一个1到10十个数字随机排列的数组
- 每日新闻 | 特斯拉“太空跑车”完成第一圈绕日飞行 向火星靠近
- Pie Chart(nvd3)
热门文章
- OutputFormat接口实现类
- HDFS 2.7.4中hdfs-site.xml参数未配置引发的一些异常
- 突然想写一篇有关欧拉函数的博客
- spring-data-redis和jedis用法、区别
- TortoiseGit:记住用户名和密码
- Scrum Meeting---Four(2015-10-28)
- 【自学笔记】定义一个标准的WPF窗口
- 支付宝支付 第四集:配置类的定义和注入
- oracle 延迟段,oracle - 未启用延迟段创建功能(ORA-00439) - 堆栈内存溢出
- java程序实验总结_Java Socket 编程实验总结