12 Bitmap的加载和Cache

2019/03/06 posted in  Android开发艺术探索

12.1 Bitmap的高效加载

BitmapFactory类提供了四类方法:decodeFile、decodeResource、decodeStream和decodeByteArray,分别用于支持从文件系统、资源、输入流以及字节数组中加载出一个Bitmap对象,其中decodeFile和decodeResource又间接调用了decodeStream方法,这四类方法最终是在Android的底层实现的,对应着BitmapFactory类的几个native方法。

如何高效地加载Bitmap 呢?其实核心思想也很简单,那就是采用BitmapFactory.Options来加载所需尺寸的图片。通过BitmapFactory.Options来缩放图片,主要是用到了它的inSampleSize参数,即采样率。

通过采样率即可有效地加载图片,那么到底如何获取采样率呢?获取采样率也很简单,遵循如下流程:

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

这里说明一下inJustDecodeBounds参数,当此参数设为true时,BitmapFactory 只会解析图片的原始宽/高信息,并不会去真正地加载图片,所以这个操作是轻量级的。

12.2 Android中的缓存策略

缓存策略:内次缓存+存储缓存。当应用打算从网络上请求一张图片时,程序首先从内存中去获取,如果内存中没有那就从存储设备中去获取,如果存储设备中也没有,那就从网络上下载这张图片。

一般来说, 缓存策略主要包含缓存的添加、获取和删除这三类操作。删除缓存目前常用的一种缓存算法是LRU(Least Recently Used),LRU是近期最少使用算法,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。采用LRU算法的缓存有两种:LruCache和DiskLruCache,LruCache用于实现内存缓存,而DiskLruCache则充当了存储设备缓存。

12.2.1 LruCache

LruCache它是一个泛型类,它内部采用一个LinkedHashMap,当强引用的方式存储外界的缓存对象,其提供了get和put方法来完成缓存的获取和添加操作,当缓存满时,LruCache会移除较早使用的缓存对象,然后再添加新的缓存对象。

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

LruCache是线程安全的,因为用到了LinkedHashMap。

  • sizeOf 计算缓存对象的大小
  • LruCache.get(key) 获取一个缓存对象
  • LruCache.put(key, bitmap) 添加一个缓存对象
  • LruCache.remove删除一个指定的缓存对象。
  • entryRemoved 移除旧缓存时会调用
    • 可以进行一些资源回收工作

12.2.2 DiskLruCache

DiskLruCache用于实现存储设备缓存,即磁盘存储,它通过将缓存对象写入文件系统从而实现缓存的效果。

  1. DiskLruCache 的创建 open
  2. DiskLruCache 的缓存添加 edit commit abort
  3. DiskLruCache的缓存查找和删除 get remove delete

12.2.3 ImageLoader的实现

一般来说,一个优秀的ImageLoader应该具备如下功能:

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

实现步骤:

  1. 图片压缩功能的实现
    1. 根据宽高修改BitmapFactory.Options采样率
  2. 内存缓存和磁盘缓存的实现
    1. LruCache和DiskLruCache
  3. 同步加载和异步加载接口的设计
    1. 使用线程池和Handler

12.3 ImageLoader的使用

12.3.1 照片墙效果

12.3.2 优化列表的卡顿现象

  • 不要在getView中执行耗时操作。
  • 控制异步任务的执行频率。可以考虑在列表滑动的时候,停止加载图片,尽管这个过程是异步的,等列表停下来以后在加载图片仍然可以获得良好的用户体验。
  • 开启硬件加速可以解决莫名的卡顿问题,通过设置android:hardwareAccelerated = "true"即可为Activity开启硬件加速。