Bitmap 的加载和 Cache

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

内容简介: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 示例可以参考 源码


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

查看所有标签

猜你喜欢:

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

民事诉讼程序研究

民事诉讼程序研究

乔罗威茨 / 吴泽勇 / 2008-6 / 40.00元

《民事诉讼程序研究》共分为诉讼程式;扩散利益、分散利益和集体利益的保护;程式样式;当事人与法官;对判決的救济;程式改革。主要內容包括:民事诉讼;英美民事诉讼程式在20世纪的若干发展;论民事诉讼法的本质和目的等。一起来看看 《民事诉讼程序研究》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码