Bitmap 的加载和 Cache

栏目: Android · 发布时间: 7年前

内容简介:Android 中如何高效地加载 Bitmap 是一个很重要也很容易被我们忽视的问题。DiskLruCache 用于实现存储设备缓存,即磁盘缓存,它通过将缓存对象写入文件系统从而实现缓存的效果。项目地址:一个优秀的图片加载器应具备:

Android 中如何高效地加载 Bitmap 是一个很重要也很容易被我们忽视的问题。

Bitmap 的高效加载

  1. BitmapFactory 类提供了: decodeFiledecodeResourcedecodeStreamdecodeByteArray 以及 decodeFileDescriptor 等几类方法来加载一个 Bitmap 对象。
  2. 高效加载 Bitmap 的核心思想就是设置 BitmapFactory.OptionsinSampleSize 采样率属性来加载所需尺寸的图片。

    inSampleSize = 1 时加载原图,inSampleSize = 2 时加载的像素为原图的 1/4,以此类推。官方推荐设置 inSampleSize 的值为 2 的指数。

  3. 获取采样率的流程:

    1. BitmapFactory.OptionsinJustDecodeBounds 参数设为 true 并加载图片。
    2. BitmapFactory.Options 中取出图片的原始宽高信息,它们对应于 outWidthoutHeight
    3. 根据采样率的规则并结合目标 View 的所需大小计算出采样率 inSampleSize
    4. BitmapFactory.OptionsinJustDecodeBounds 参数设为 false 然后重新加载图片。

      示例代码
       public Bitmap decodeSampledBitmapFromResource(Resources res,
              int resId, int reqWidth, int reqHeight) {
          // 设置 inJustDecodeBounds = true 时 BitmapFactory 只会解析图片的原始宽高等信息。
          // 并不会真正地去加载图片
          final BitmapFactory.Options options = new BitmapFactory.Options();
          options.inJustDecodeBounds = true;
          BitmapFactory.decodeResource(res, resId, options);
      
          // 计算采样率
          options.inSampleSize = calculateInSampleSize(options, reqWidth,
                  reqHeight);
      
          // 重新加载图片
          options.inJustDecodeBounds = false;
          return BitmapFactory.decodeResource(res, resId, options);
      }
      
      public int calculateInSampleSize(BitmapFactory.Options options,
              int reqWidth, int reqHeight) {
          if (reqWidth == 0 || reqHeight == 0) {
              return 1;
          }
      
          // 图片的原始宽高信息
          final int height = options.outHeight;
          final int width = options.outWidth;
      
          int inSampleSize = 1;
      
          if (height > reqHeight || width > reqWidth) {
              final int halfHeight = height / 2;
              final int halfWidth = width / 2;
      
              while ((halfHeight / inSampleSize) >= reqHeight
                      && (halfWidth / inSampleSize) >= reqWidth) {
                  inSampleSize *= 2;
              }
          }
      
          return inSampleSize;
      }
      

Android 中的缓存策略

  1. 缓存策略并没有统一的标准,一般来说缓存策略主要包含缓存的添加、获取和删除这三类操作。
  2. 常用的缓存算法是 LRU (Least Recently Used),翻译为:近期最少使用算法。它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。
  3. Android 中常见的 LRU 算法缓存有两种: LruCache (内存缓存) 和 DiskLruCache (存储设备缓存),一般情况下会将两者结合使用。

LruCache

  1. LruCache 是一个泛型类,内部采用一个 LinkedHashMap<K, V> 以强引用的方式存储外界的缓存对象,提供了 getput 等操作方法,当存储满时,会移除较早使用的缓存对象,再添加新的缓存对象。此外,LruCache 是线程安全的。

    三种引用的区别:

    • 强引用 :直接的对象引用
    • 软引用 :当一个对象只有软引用存在时,系统内存不足时此对象会被 gc 回收
    • 弱引用 :当一个对象只有弱引用存在时,此对象会随时被 gc 回收
  2. LruCache 典型示例代码:

    // 获取当前可用的最大内存
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // KB
    // 设置缓存大小
    int cacheSize = maxMemory / 8;
    
    LruCache<String, Bitmap> lruCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap value) {
            // 重写此方法计算缓存对象的大小
            return value.getRowBytes() * value.getHeight() / 1024;
        }
    };
    // 添加缓存
    lruCache.put("liyu", bitmap);
    // 获取缓存
    Bitmap bitmap = lruCache.get("liyu");
    

DiskLruCache

DiskLruCache 用于实现存储设备缓存,即磁盘缓存,它通过将缓存对象写入文件系统从而实现缓存的效果。项目地址: https://github.com/JakeWharton/DiskLruCache

  1. DiskLruCache 的创建

     /**
    * @param directory 缓存路径
    * @param appVersion 一般为 1,当版本号变更时,会清空缓存文件
    * @param valueCount 单个节点所对应的数据个数,一般为 1
    * @param maxSize 缓存总大小,超过的话会清除一些缓存
    */
     public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
    
  2. DiskLruCache 的缓存添加

    String key = hashKeyFormUrl(url); // url 转换下防止特殊字符影响
    DiskLruCache.Editor editor = mDiskLruCache.edit(key);
    if (editor != null) {
        OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX); // DISK_CACHE_INDEX 为 0,因为设置的一个节点只有一个数据
        if (downloadUrlToStream(url, outputStream)) { //下载
            editor.commit(); // 下载成功提交缓存
        } else {
            editor.abort(); // 出现异常则取消缓存
        }
        mDiskLruCache.flush(); // 强制缓冲文件保存到文件系统
    }
    
  3. DiskLruCache 的缓存查找

    Bitmap bitmap = null;
    String key = hashKeyFormUrl(url); // url 转换下防止特殊字符影响
    DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key); // 获取缓存快照
    if (snapShot != null) {
        FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(DISK_CACHE_INDEX); // DISK_CACHE_INDEX 为 0,因为设置的一个节点只有一个数据
        FileDescriptor fileDescriptor = fileInputStream.getFD(); // 解决 decodeStream 缩放 bitmap 第二次为 null 的问题
        bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescriptor,
                reqWidth, reqHeight); // 缩放图片
        if (bitmap != null) {
            addBitmapToMemoryCache(key, bitmap); // 添加到内存缓存,方便下次快速获取
        }
    }
    

ImageLoader 的实现

一个优秀的图片加载器应具备:

  • 图片的同步加载
  • 图片的异步加载
  • 图片压缩
  • 内存缓存
  • 磁盘缓存
  • 网络拉取

完整的 ImageLoader 示例可以参考 源码


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

RESTful Web Services Cookbook

RESTful Web Services Cookbook

Subbu Allamaraju / Yahoo Press / 2010-3-11 / USD 39.99

While the REST design philosophy has captured the imagination of web and enterprise developers alike, using this approach to develop real web services is no picnic. This cookbook includes more than 10......一起来看看 《RESTful Web Services Cookbook》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具