Android开发艺术探索——Bitmap的加载和Cache

这是我在学习过程中总结的知识
目的是希望日后回来看或者需要用的时候可以 一目了然 # 的回顾、巩固、查缺补漏
不追求详细相当于书本的精简版或者说是导读(想看详细的直接对应翻书),但会尽力保证读者都能快速理解和快速使用(随理解加深会总结的更加精简),但必要时会附上一些较详细解释的链接
脚注是空白的:表示还没弄懂的知识,了解后会添加

文章目录

    • @[TOC]
  • 12.1 Bitmap 的高效加载
  • 12.2 Android中的缓存策略
    • 12.2.1 LruCache
    • 12.2.2 DiskLruCache
    • 12.2.3 ImageLoader的实现
  • 12.3 ImageLoader的使用
    • 12.3.2 优化卡顿现象

本章主要内容:

  1. 如何有效加载一个Bitmap——因为Android对单个应用所施加的内存限制,导致加载Bitmap的时候很容易出现内存溢出
  2. 常用的缓存策略——避免每次都从网络是请求图片或者从存储设备号中加载图片
  3. 优化列表的卡顿现象
    本章提供一个示例程序,涵盖以上3个主题

12.1 Bitmap 的高效加载

通常如何加载一个Bitmap?

Bitmap指的是一张图片,png或者jpg
BitmapFactory提供了四类方法
decodeFile、decodeResource、decodeStream、decodeByteArray
对应从文件系统、资源、输入流、字节数组中加载出一个Bitmap对象
decodeFile、decodeResource又间接调用了decodeStream方法

如何高效地加载Bitmap?

核心思想是采用BitmapFactory.Options来加载所需尺寸的图片
比如通过ImageView来显示图片,通常ImageView没有图片原始尺寸这么大,这时就通过BitmapFactory.Options按一定的采样率来加载缩小后的图片,降低内存占用

BitmapFactory.Options

BitmapFactory.Options缩放图片,主要是用到了 inSampleSize 参数,即采样率
inSampleSize为1时,采样为原始大小
inSampleSize 为2时,图片的宽高为原图的1/2,像素数为1/4,占用内存为1/4
inSampleSize 为4,缩放比例就是1/16,即2的4次方
inSampleSize应该为2的指数倍,2,4,8,16等
inSampleSize小于1,相当于1
inSampleSize不为2 的指数,向下取整为2的指数

如何获取采样率?

  1. 将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片
  2. 设置为true后BitmapFactory只会解析图片的原始宽高信息,并不会加载图片
  3. 根据结果设置合适的采样率
  4. inJustDecodeBounds设回false然后重新加载图片

例子

 public Bitmap decodeSampledBitmapFromFileDescriptor(FileDescriptor fd, int reqWidth, int reqHeight) {
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFileDescriptor(fd, null, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth,
                reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFileDescriptor(fd, null, options);
    }

    public int calculateInSampleSize(BitmapFactory.Options options,
            int reqWidth, int reqHeight) {
        if (reqWidth == 0 || reqHeight == 0) {
            return 1;
        }

        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        Log.d(TAG, "origin, w= " + width + " h=" + height);
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and
            // keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) >= reqHeight
                    && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }

        Log.d(TAG, "sampleSize:" + inSampleSize);
        return inSampleSize;
    }

12.2 Android中的缓存策略

缓存可以避免过多的消耗流量
当用户第一次从网上加载图片后,会把图片缓存在内存中,再缓存到本地储存设备.这样当应用打算从网络请求一张图片的时候会先访问内存,再访问储存设备,都没有后才会去下载
目前常用的缓存算法是LRU,近期最少使用算法

12.2.1 LruCache

  • LruCache用于实现内存缓存
  • 建议采用support-v4兼容包提供的LruCache
  • LruCache是一个泛型类,内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象

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

LruCache 的实现

需要提供缓存的总容量大小并重写sizeOf方法(计算缓存对象的大小)
LruCache还支持删除操作,通过remove方法即可删除一个指定的缓存对象

12.2.2 DiskLruCache

  • 用于实现存储设备缓存
  • 将缓存对象写入文件系统

1. DiskLruCache 的创建
DiskLruCache并不能通过构造方法来创建,它提供了open方法用于创建自身

    public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)

· 第一个参数表示磁盘缓存在文件系统中的存储路径,可以选择SD卡.(如果希望应用卸载后删除缓存文件,那么就选择SD卡上的缓存目录,否则应该选择SD卡上的其他特定目录)
· 第二个参数表示应用的版本号,一般为1,版本号改变时会清空之前所有的缓存文件
· 第三个参数表示单个节点所对应的数据个数,一般为1
· 第四个表示缓存的总大小,比如50MB

2. DiskLruCache的缓存添加

  • 此操作是通过Editor来完成的
  • 缓存首先要获取图片的url所对应的key(url的md5值)
  • 缓存只允许编辑一个缓存对象
  • 通过Editor对象得到一个文件输出流,写入到文件系统上
  • 最后Editor.commit()

3. DiskLruCache的缓存查找

  • 查找也需要将url转换为key
  • 然后通过DiskLruCache的get方法得到哟呵Snapshot对象
  • 再通过Snapshot对象得到缓存的文件输入流
  • 记得通过BitmapFactory.Options加载一个缩放后的图片

12.2.3 ImageLoader的实现

一个优秀的ImageLoader应具备如下功能:

  • 图片的同步加载(从内存缓存、磁盘缓存、网络中获取的)
  • 图片的异步加载(ImageLoader内部需要自己在线程中加载图片并将图片设置给所需的ImageView)
  • 图片压缩
  • 内存缓存
  • 磁盘缓存
  • 网络拉取

ImageLoader还需要处理在ListView或者GridView中,快速下拉时图片在item错位的情况

书本P425页具体一步步实现一个ImageLoader,综合了11章线程池内容,内存缓存和磁盘缓存的实现、同步异步加载的接口的设计等内容,可以做很好的回顾和深入了解

12.3 ImageLoader的使用

本节将演示如何通过ImageLoader实现一个照片墙的效果,见书P441

12.3.2 优化卡顿现象

  • 关键是不要在主线程中做太多耗时的操作
  • 不要在getView中执行耗时操作(如加载图片)
  • 控制异步任务的执行频率,比如频繁的上下滑动会产生N个异步任务.这时可以·考虑在列表滑动的时候停止加载图片,停下来再加载
  • 开启硬件加速:android:hardwareAccelerated=“true”

你可能感兴趣的:(Android理论类)