Bitmap的加载和Cache
android对单个应用内存限制,比如16M,最新版本或许可以128M,具体以最新的API为准,这就会导致Bitmap加载的时候很容易内存溢出。
Bitmap一般都采用缓存策略,避免多次加载,常用的缓存由lruCache和DiskLruCache两种,一个是内存缓存,一个是存储缓存。
列表需要加载大量子视图的时候,用户快速滑动很容易出现卡顿,如何优化是很重要的。
12.1 Bitmap的高效加载
BitmapFactory提供四类方法:decodeFile、decodeResource、decodeStream和decodeByteArray,分别支持文件、资源、输入流和字节流中加载出一个Bitmap对象,前面两个其实也是间接调用的decodeStream方法。
BitmapFactory提供裁剪的方法,可以按照一定的采样率来缩小图片大小,这样可以一定程度上降低内存一定程度上避免OOM。
获取采样率流程:
1.BitmapFactory.Options的inJusDecodeBounds参数设置为true并加载图片
2.BitmapFactory.Options中取出原始的图片宽高
3.根据采样率规则并结合目标View的大小计算采样率inSampleSize
4.BitmapFactory.Options的inJusDecodeBounds设置false,然后重新加载图片
设置inJusDecodeBounds的原因是这个加载图片是轻量级的,不会真的去全部加载,获取信息而已。
12.2 Android中的缓存策略
缓存策略主要包括缓存的添加、获取和删除,主要用到的算法是LRU,也就是近期最少使用算法。
常用的一些框架也可以参考一下,比如ImageLoader,很多网络框架本身也集成了图片框架,可以留意最新的消息。
12.2.1 LruCache
LruCache是线程安全的,内部使用的是linkedHashMap集合,使用比较简单,就是根据KEY获取和设置缓存对象就可以了,remove方法可以移除一个指定缓存对象。有兴趣可以看一下相关源码
12.2.2 DiskLruCache
DiskLruCache是磁盘缓存,他可以将缓存对象写入文件系统中。
1.DiskLruCache的创建
DiskLruCache不能直接构造,可以通过open方法来创建自身。open方法有四个参数,其中分别是:缓存的存储路径、应用的版本号、单个节点对应的数据个数和缓存的总大小。
2.DiskLruCache的缓存添加
DiskLruCache的缓存操作都是通过Editor完成的,Editor表示一个缓存对象的编辑对象,也是一样需要图片对应的KEY,然后根据KEY来获取Editor编辑对象,可以使用文件的MD5来作为KEY。在编辑完成后需要Editor去commit来提交写入操作,类似SP文件操作。
3.DiskLruCache 缓存查找
和添加流程类似,根据URL转换为KEY,根据EKY获取到snapshot对象,然后获取缓存的文件输入流,最后转换成为Bitmap对象。
12.2.3 ImageLoader的实现
ImageLoader具备以下功能:
1.图片的同步和异步加载
2.图片压缩
3.内存缓存
4.磁盘缓存
5.网络拉取
内存缓存和磁盘缓存是ImageLoader的核心,还有就是列表里面的ITEM也是需要考虑的,因为他们内部View是会复用的,需要特殊处理。
ImageLoader的实现步骤:
1.图片压缩功能的实现
主要是ImageResizer这个类实现的,具体逻辑可以参考前面讲的,或者看最新代码。
2.内存和磁盘缓存的实现
ImageLoader初始化的时候就会创建LruCache和DiskLruCache,分别来完成内存缓存和磁盘缓存的工作。磁盘缓存上限50M,取决于当前进程可以使用的内存上限,具体情况具体分析。
3.同步加载和异步加载的接口设计
同步加载:从loadBitmap中看出,工作流程主要是首先尝试从内存缓存中读取,然后是磁盘缓存,最后才是网络拉去,都是在线程中处理的,通过检查当前线程的Looper是否是主线程的Looper来判断当前线程是否是主线程,不是就会抛出异常。
异步加载:从bindBitmap看,流程基本和同步一直,区别在于图片加载成功后需要绑定到ImageView封装成一个LoaderResult对象,然后通过mMainHandler来向主线程中发送一个小希,这样就可以在主线程中给ImageView来设置图片了。
加载一般用线程池,主要是避免在列表的时候创建过多的线程,一般配置核心线程是当前设置的CPU核心数+1,最大容量为CPU核心的2倍+1,线程超时时长为10秒。
ImageLoader使用的Handler是直接采用主线程里面的Looper来构成的Handler对象,这样使得ImageLoader也可以在非主线程中构造。
为了解决View复用导致列表错位这一个问题,在给ImageView设置图片之前都会检查它的URL有没有发生改变,如果发生改变就不在设置。
12.3 ImageLoader的使用
具体使用参考开源库的最新API,这样跳过就不做陈述了。
12.3.1照片墙效果
核心代表就是mImageLoader.bindBitmap(uri,imageView,mImageVidth,mImageWidth),剩下的就交给ImageLoader来实现了。
12.3.2 优化列表的卡顿现象
不要在主线程中做耗时操作。
1.不在getView的时候执行加载图片等耗时操作
2.控制异步执行的频率,建议是等UI更新完成后再去加载,当列表处于滑动中先等待,等滑动停止了在加载图片。
3.可以开启硬件加速。