本文基本上是翻译自developer.android的教程。这个教程我一直要看的都没捞到看,今天看了一下,下面是大意的总结。如果想要详细了解的话请看原文和实例代码:
http://developer.android.com/training/displaying-bitmaps/index.html
下面是我的总结:
1. 当你的bitmap很大的时候,分辨率和像素远远大于你要展示的终端,那么可以考虑使用BitmapFactory.options 变量来进行剪裁适合的分辨率版本的bitmap.
2. 把加载bitmap放入一个asyncTast中去,并且在这个asyncTask中添加一个WeakReference<ImageView>来保证不会被垃圾回收器回收(想象一个极端情况,你要设为背景的bitmap终于处理完了,结果带有相应要使用的imageview已经不在使用了(比如跳到其他的activity了早已),这时候可能会出现null pointer了,所以持有一个weakreference并且使用前检查一下还存在不)
3. 在ListView和GridView中不能单纯使用AsyncTask, 因为不知道什么时候会加载,很可能出现了对应的view都消失了,图片还没加载完的问题,而且图片要求及时加载的。
解决的方法是,给imageView再添加一个对AsyncTask的引用(还记得第二步里面,我们的AsyncTask拥有对ImageView的引用了么?)这里的逻辑是这样的:
第一步:新建一个Drawable的子类,比如继承BitmapDrawable,命名为AsyncDrawable. 这个AsyncDrawable和BitmapDrawable一样,唯一的区别是它多了一个到上面AsyncTask的引用。
第二步:设置imageView.setImageDrawable( AsyncDrawable ); 这样,这个ImageView就拥有了相应AsyncTask的引用。
第三步:每次调用AsyncTask的时候,检查该ImageView的asyncTask是否在进行中,如果正在进行就取消掉;然后调用新的AsyncTask来执行当前的ImageView。ImageView得到相应的AsyncTask的方法就是ImageView.getDrawable, 然后这个Drawable再得到AsyncTask即可。
4. 对于ListView和GridView来说,每次滑动都会导致大量的View创建和销毁动作。相应带来大量的Bitmap瞬间加载后又瞬间销毁。解决方法是使用一个LruCache。使用cache之前先想好怎么用,然后给这个cache分配相应的size. 这个cache的用法很简单,就是一个map类型。
使用的时候,在AsyncTask中间,设置完bitmap后顺便把它放到LruCache里面。
有时候放在内存里面的cache并不可靠,因为用户可能会切换到其他的程序然后再打开,这时候就要重新加载一遍了;这个时候可以考虑把cache放到硬盘上面,详细方法不说了。注意content provider有时候加载起来更加有效。
除了切换应用,还有时候可能屏幕旋转,这个时候可以把imageview放到Fragment里面,然后重新attach就可以了。
5. 除了设置了cache以外,针对不同的android版本,还可以设置不同的内存管理方式。
Android 2.3.3 主要依赖主动调用recycle(). 使用一个displayed 计数器和一个 reference计数器来决定recycle的回收时机。
Android 3.0 及以上, 使用LruCache的inBitmap属性,来重用bitmap.
6. 总结:事实上,如何更有效的展示bitmap,基本上可以概括如下:
a) 对于像素和分辨率过大的bitmap, 采用BitmapFactory.options可以载入适合屏幕展示的bitmap
b) 将适用于GridView、ViewPager、ListView中载入bitmap的地方放入一个asyncTask来载入。
c) AsyncTask中,持有ImageView的引用防止其回收;
d) ImageView中,设置一个自定义的drawable, 这个drawable持有该ImageView正在使用的AsyncTask. 这时候,你就可以设定一个默认的bitmap来作为imageView的背景,而且一旦bitmap载入完成,就可以替换为真正要展示的bitmap了。相当于先给ImageView设定一个缓存图片来显示,真正要显示的图片在后台加载,加载完成后再替换为真正的图片。
e) 经过笔者实际测试,当载入图片为小图片的时候,上述所有的步骤全部都不需要,android本身还是非常流畅的载入和读取的;笔者测试了载入将近100张比较小的图片(不是那种单反拍摄的,一般几十k到几百k),都没有问题。但是,当载入图片改成单反拍摄的图片后,使用GridView会导致滑动非常不流畅,而且会闪退(应该是内存不够用)。
f) 所以,这种复杂而且比较麻烦的方法只适用于当载入的图片比较大,或者来自网络,需要时间来加载的时候。对于小图片来使用gridview或者viewpager就完全没必要了。