第十二章 Bitmap 的加载和 Cache
Bitmap 的高效加载
- 由于Bitmap的特殊性以及Android 对单个应用所施加的内存限制,比如16MB,这导致加载 Bitmap 的时候很容易出现内存溢出。
- 加载图片:BitmapFactory 类提供了四类方法,
decodeFile、decodeResource、decodeStream、decodeByteArray
,分别用于支持从文件系统、资源、输入流以及字节数组中加载出一个Bitmap 对象,decodeFile、decodeResource
间接调用了decodeStream
,这四类方法最终是在Android的底层实现的,对应着BitmapFactory
的几个native方法
3. 如何高效地加载图片?
采用BitmapFactory.Options
来加载所需尺寸的图片,假设通过imageView
来显示图片,很多时候ImageView
并没有图片的原始尺寸那么大,整张图片加载进来再设置给ImageView
显然没必要,通过BitmapFactory.Options
就可以按一定的采样率来加载缩小后的图片,将缩小后的图片在ImageView
中显示,这样就会降低内存占用从而在一定程度上避免OOM,提高了Bitmap加载时的性能。 - 通过
BitmapFactory.Options
来缩放图片,主要是用到了inSampleSize
参数,即采样率。当 inSampleSize 为1时,采样后的图片和原始图片大小相同;当 inSampleSize 为2时,采样后的图片其宽高均为原图大小的1/2,像素数为原图大小的1/4。
如何获取采样率?
(1) 将BitmapFactory.Options
的inJustDecodeBounds
参数设为true并加载图片
(2) 从BitmapFactory.Options
中取出原始宽高信息,对应于outWidth、outHeight
(3) 根据采样率的规则并结合目标view的所需大小计算出采样率inSampleSize
(4) 将BitmapFactory.Options
的inJustDecodeBounds
设为 false 然后重新加载图片inJustDecodeBounds
参数 当此参数为 true 时,BitmapFactory
只会解析图片的宽高信息,并不会真正地加载图片,该操作是轻量级的。此时BitmapFactory
获得的图片宽高信息和图片的位置以及程序运行的设备有关,比如一张图片放在不同的 drawable 目录下,或者程序运行在不同屏幕密度的设备上,都会导致 BitmapFactory 获取到不同的结果,原因是和 Android 的资源加载机制有关。
BitmapFactory.decodeResource();
BitmapFactory.decodeFile();
BitmapFactory.decodeStream();
BitmapFactory.decodeByteArray();
以上四个方法都是支持采样率加载的,处理方式也是类似的。decodeStream()
有些特殊,需要获取流的文件描述符FileDescriptor
,通过BitmapFactory.decodeFileDescriptor
加载。
Android 中的缓存策略
1. 如何避免过多的流量消耗呢?
缓存。当程序第一次从网上加载图片后,就将其缓存到设备上,为了提高用户体验,往往还会想内存中缓存一份。当应用打算从网络上请求一张图片时,程序会首先从内存中获取,如果内存中没有就从设备中获取,如果设备中没有,就从网络上下载。该缓存策略不仅适用于图片,也适用于其他文件类型。
2. 常用缓存算法:LRU。LRU是近期最少使用算法,核心思想:当缓存满时,会优先淘汰那些近期最少使用的缓存对象。采用LRU算法的缓存有两种:LruCache
和 DiskLruCache
LruCache
内部采用一个LinkedHashMap
以强引用的方式存储外界的缓存对象。当缓存满的时候,LruCache 会移除较早使用的缓存对象,然后再添加对象。LruCache 是线程安全的。
强引用:直接的对象引用
软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被 gc 回收
弱引用:当一个对象只有弱引用存在时,此对象会随时被gc回收
DiskLruCache
磁盘缓存。
ImageLoader 的实现
一个优秀的ImageLoader
应该具备如下功能:
图片的同步加载;图片的异步加载;图片压缩;内存缓存;磁盘缓存;网络拉取
图片的同步加载:以同步的方式向调用者提供所加载的图片,该图片可能是从内存、磁盘和网络中拉取的。
图片的异步加载:调用者无需在单独的线程中以同步的方式获取图片,此时ImageLoader
需要自己在线程中加载图片并将图片设置给所需的ImageView
图片压缩:降低OOM概率的有效手段
内存缓存、磁盘缓存:极大提高程序效率,有效降低用户流量。
列表错位:对于ListView、GridView
等,由于 itemView 的复用导致的列表错位。
优化列表的卡顿现象
不要在主线程做太多的耗时操作即可提高滑动的流畅度。
1. 不要在getView
中执行耗时操作。
2. 控制异步任务的执行频率。
如果用户可以频繁上下滑动,就会一瞬间产生上百个异步任务,这些任务会造成线程池的拥堵并随即带来大量的UI更新操作,造成一定程度的卡顿?
解决:可以考虑在列表滑动的时候停止加载图片,开启硬件加速。
上一篇: 无坚不摧 唯快不破
下一篇: JAVA多线程读写文件范例