最近有个ListView需要加载大量的图片,图片的大小也相对比较大。后来发现这个列表会比较卡,觉得需要学习一下图片相关的知识来解决这个问题。毕竟在android这样的嵌入式系统中,内存相对有限,图片又相当的占内存,如果出现溢出等问题,就极有可能造成程序的性能受到影响。
一、 首先我想搞清楚的问题是,android GC相关的日志信息,因为我在调试这块程序时,发现GC很频繁,而且每次GC都列表感觉都会卡一下。
我频繁出现的gc都是如下图这样:
根据查阅资料,我发现android gc的log信息有如下几类:
GC_EXTERNAL_ALLOC: Means thatthe VM is trying to reduce the amount of memory used collectable objects, tomake room fornon-collectable objects.
GC_FOR_MALLOC: Means thatthe GC was triggered because there wasn't enough memory left on the heap toperform an allocation.Might be triggered when new objects are being created.
GC_CONCURRENT: Triggeredwhen the heap has reached a certain amount of objects to collect.
GC_EXPLICIT:
所以,我频繁的gc是外部的内存(native内存)需要释放,现在是3126k,释放了1512k,暂停了73ms。
二、我想找是什么原因造成这块内存需要频繁的gc,造成native内存的不断增大。
如下图示,android的在Honeycomb(android3.0)之前是将bitmap放在native内存的,它的释放是通过recycle或finalizer,3.0以后会在堆内存分配。我的列表图片比较大(使用android2.3),频繁释放肯定是跟bitmap有关系。
三、我接着想弄明白一个bitmap到底占用多大的内存,它跟图片的格式(jpg、png等)有特定的关系吗?从而来减小占用的内存。
其实,一张图片(bitmap)的内存大小 = 图片高度 * 图片宽度 * 单位像素占用的字节数
android 中可以通过bitmap.getByteCount()计算出。低版本android可以通 过bitmap.getHeight()*bitmap.getRowBytes得到。具体可以参考
http://blog.sina.com.cn/s/blog_4e60b09d01016133.html ,做了具体的介绍。
而jpg,png等是指的一种压缩格式,这样的压缩主要是为了文件的传输方面的考虑。jpg,png之所以频繁使用,是因为压缩比率比较高。所以不管图片是怎样一种传输格式传输的,最后到内存的大小与这个并无直接关系,与上面的有直接关系。但是格式跟图片的质量有直接关系。
像我的一张jpg图片,大小是450×225,假如我是用ARGB_8888 来代表一个像素的颜色,那么大小应该是450×225×4 = 405kb,这是内存中的大小,而我存在本地的jpg图片可能只是20k大。
最后,对于比较大的图片,我使用了RGB_565,图片在内存中大小为202.5k,节省了内存,损失了图片质量,节省了内存。(因为本身传输的图片jpg格式,图片只有20k左右大,有损压缩,我认为RGB_565,基本能满足展示的需要了)。
四、上面的做法对减小内存有所帮助,但是还是有问题,因为我发现很多我用SoftReference指向的图片可能并没及时释放,导致native内存一直在增加,类似溢出。针对这个情况和android3.0以前,原因在于bitmap只有显式调用recycle方法和finalizer才能及时释放。我将每个页面对应的图片,在Activity onDestroy时显示进行了释放。
经过上面的过程,我感觉应该是基本解决了我遇到的问题,希望拿来分享给遇到同样问题的朋友。
最后给大家一些链接,能有助于大家弄清楚这相关的知识,我只是粗略的写了些:
http://www.youtube.com/watch?v=_CruQY55HOk&feature=player_detailpage#t=699s
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html