前言:

上篇我们总结了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算法相关推荐

  1. android图片缓存,直接应用项目中的Android图片缓存技术

    前不久搞的Android图片缓存,刚开始引入开源的框架,用着还行,但是在开发中遇到问题,就比如universal-image-loader-1.9.5.jar这个框架吧,在加载图片的时候自定义imag ...

  2. Android 图片缓存之内存缓存技术LruCache,软引用

    Android 图片缓存之内存缓存技术LruCache,软引用

  3. Android图片缓存框架Glide

    Android图片缓存框架Glide Glide是Google提供的一个组件.它具有获取.解码和展示视频剧照.图片.动画等功能.它提供了灵活的API,帮助开发者将Glide应用在几乎任何网络协议栈中. ...

  4. Android 图片缓存机制

    1.采用线程池  2.内存缓存+文件缓存  3.内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的1/4  4.对下载的图片进行按比例缩放,以减少内存 ...

  5. Android图片缓存管理

    在一个app中,图片资源是处处存在的,加载图片的流程一般是: 1 先从缓存中读取 2 若缓存中不存在,从SD卡中读取 3 若SD卡中也不存在,则从服务器拉取 后两个步骤纯碎属于业务逻辑,暂且不表,这里 ...

  6. android 图片缓存工具类,Android工具类系列-Glide图片缓存与圆角

    Glide的图片缓存和清除图片缓存 public class GlideCacheUtil { private static GlideCacheUtil inst; public static Gl ...

  7. android 图片缓存

    一.Picasso https://github.com/square/picasso Picasso是Square公司开源的一个Android平台上的图片加载框架,简单易用,一句话搞定项目中的图片加 ...

  8. Android图片缓存框架 - Fresco的GenericDraweeHierarchy (五)

    目录 1.Fresco 简介 2. Fresco 文档 3. Fresco开发步骤 4. Fresco加载图片6种方式 5. Drawees xm属性设置 6 Fresco实现圆角或圆形图片 7. D ...

  9. Android图片缓存框架 - Fresco实现圆角或圆形图片 (四)

    目录 1.Fresco 简介 2. Fresco 文档 3. Fresco开发步骤 4. Fresco加载图片6种方式 5. Drawees xm属性设置 6 Fresco实现圆角或圆形图片 6.1 ...

最新文章

  1. eclipse常用快捷键Get;set;
  2. [Java] 02 String的常用方法
  3. React创建组件的三种方式及其区别
  4. Subversion基本操作
  5. Mysql 基本框架 select的全过程
  6. oracle 批量修改表结构,关于Oracle批量修改表结构相关内容的整理
  7. STM32(五)------GPIO位带操作
  8. Linux下怎么使用任务管理器和真人接口源码出售进程管理
  9. 零基础Python知识点回顾(三)
  10. [25年后的统计系会是什么样?
  11. C/c++输入输出函数
  12. labview霍夫曼编码_毕业设计 基于LabVIEW的编码的设计与仿真—信源编码
  13. iOS 基础入门--Bull' Eye 小游戏 
  14. Kubesphere-多节点安装
  15. static(静态变量,方法)
  16. PHP中一些通用和易混淆技术点的最佳编程实践
  17. LiteOS 学习第一篇
  18. java生成1到10的随机数_用java生成一个1到10十个数字随机排列的数组
  19. 每日新闻 | 特斯拉“太空跑车”完成第一圈绕日飞行 向火星靠近
  20. Pie Chart(nvd3)

热门文章

  1. OutputFormat接口实现类
  2. HDFS 2.7.4中hdfs-site.xml参数未配置引发的一些异常
  3. 突然想写一篇有关欧拉函数的博客
  4. spring-data-redis和jedis用法、区别
  5. TortoiseGit:记住用户名和密码
  6. Scrum Meeting---Four(2015-10-28)
  7. 【自学笔记】定义一个标准的WPF窗口
  8. 支付宝支付 第四集:配置类的定义和注入
  9. oracle 延迟段,oracle - 未启用延迟段创建功能(ORA-00439) - 堆栈内存溢出
  10. java程序实验总结_Java Socket 编程实验总结