Android 开发艺术探索笔记之十二 -- Bitmap 的加载和 Cache

学习内容:

  • 如何有效加载 Bitmap
  • Android 常用的缓存策略
    • LurChche - 内存缓存
    • DiskLurCache - 存储缓存
  • 优化列表的卡顿现象




1. Bitmap 的高效加载

如何加载图片?

四类方法:

  1. BitmapFactory.decodeFile / decodeResource / decodeStream / decodeByteArray
  2. 分别对应从 文件系统 / 资源 / 输入流 / 以及字节数组 中加载 Bitmap 对象
  3. 关系:decodeFile 和 decodeResource 间接调用了 decodeStream

如何高效加载图片?

  1. 核心思想:采用 BitmapFactory.Options 加载所需尺寸的图片
  2. 说明:主要是 inSampleSize 参数,即采样率。inSampleSize 为 1 时,表示原始大小;当 inSampleSize 为 2 时,采样后的图片 宽/高 均变为原来的 1/2,即整个图片缩小为原来的 1/4。inSampleSize 必须大于 1 才能起作用,效果以此类推。
  3. 具体方法(获取采样率):

    1. 将 BitmapFactory.Options 的 inJustDecodeBounds 参数设为 true 并加载图片
    2. 从 BitmapFactory.Options 中取出图片的原始宽/高信息,对应于 outWidth 和 outHeight 参数
    3. 计算所需的采样率 inSampleSize
    4. 将 BitmapFactory.Options 的 inJustDecodeBounds 参数设为 false,然后重新加载图片

    inJustDecodeBounds 参数设为 true 时,BitmapFactory 只会解析图片的原始 宽/高 信息,并不会真正的加载图片




2. Android 中的缓存策略

为什么需要缓存?

两方面原因:提高程序效率 + 节约流量开销

缓存策略

一般来说,缓存策略主要包括 缓存的添加获取删除 这三个操作。
目前常用的一种缓存算法是 LRU,即最近最少使用算法,核心思想是缓存满了时,优先淘汰最近最少使用的缓存对象。

2.1 LruCache

兼容性

LruCache 是 Android 3.1 提供的一个缓存类,如果需要兼容 3.1 以下的版本,则需要使用 support-v4 兼容包中提供的 LruCache

实现思想

LruCache 是一个泛型类,内部采用一个 LinkedHashMap 以强引用的方式存储外界的缓存对象,通过 get 和 set 方法完成缓存的获取和添加,当缓存满时,LruCache 会移除较早使用的缓存对象,然后添加新的缓存对象。

LruCache 是线程安全的。

关于 强引用、软引用和弱引用:

  • 强引用:直接的对象引用
  • 软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被 GC 回收
  • 弱引用:当一个对象只有弱引用存在时,此对象随时被 GC 回收

原理

留待后续学习。

具体使用

  1. 创建:提供缓存的总容量大小并重写 sizeOf 方法
  2. 获取:get 方法
  3. 添加:put 方法
  4. 删除:remove 方法删除指定的缓存对象

2.2 DiskLruCache

简述

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

使用方式

  1. DiskLruCache 的创建

    • 通过 public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) 方法创建自身。
    • 参数:
      • directory:磁盘缓存在文件系统中的存储路径
      • appVersion:应用的版本号,一般设为 1 即可
      • valueCount:单个节点所对应的数据的个数,一般设为 1 即可
      • maxSize:缓存总大小,超过这个设定值后,会清除一些缓存
    • 典型代码如下:

      private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50; //50MB
      
      File diskCacheDir = getDiskCacheDir(mContext,"bitmap");
      if (!diskCacheDir.exists()) {
       distCacheDir.mkdirs();
      }
      mDiskLurCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
  2. DiskLruCache 的缓存添加

    • 核心:通过 Editor 完成,Editor 表示一个缓存对象的编辑对象
    • 步骤:

      1. 获取图片 url 对应的 key

        public static String hashKeyFromUrl(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;
         }
        
         private 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();
         }
        
      2. 根据 key 通过 edit() 获取 Editor 对象,得到输出流,并再下载图片时通过该输出流写入到文件系统,最后通过 commit() 提交。
        注意:此部分应当通过 子线程 执行,避免下载图片造成 ANR;

        String key = Util.hashKeyFromUrl(url);
        //得到DiskLruCache.Editor
        DiskLruCache.Editor editor = diskLruCache.edit(key);
        if (editor != null) {
            OutputStream outputStream = editor.newOutputStream(0);
            if (downloadUrlToStream(Util.IMG_URL, outputStream)) {
                publishProgress("");
                //写入缓存
                editor.commit();
            } else {
                //写入失败
                editor.abort();
            }
        }
      3. 关于下载图片:

            private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
                HttpURLConnection urlConnection = null;
                BufferedOutputStream out = null;
                BufferedInputStream in = null;
        
                try {
                    final URL url = new URL(urlString);
                    urlConnection = (HttpURLConnection) url.openConnection();
                    in = new BufferedInputStream(urlConnection.getInputStream(), IO_BUFFER_SIZE);
                    out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE);
                    int b;
                    while ((b = in.read()) != -
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值