曹帮琴,徐 昊
(信阳职业技术学院数学与计算机科学学院,河南信阳464000)
作为一款便携式终端平台,Android是以Linux内核为基础的开放源码操作系统[1],主要应用于便携设备,目前Android已经成为全球最受欢迎的智能手机平台.Android系统以其开源、开放和优异便捷的开发架构,吸引了众多程序开发者,其运行环境由核心库集和Dalvik虚拟机组成[2].核心库集提供了Java编程语言核心库的大多数功能,每一个应用程序都在它自己的进程中运行,拥有一个独立的Dalvik虚拟机实例.在编写应用程序时,可以访问Android提供的API[3],调用它的C/C++开发库及核心应用程序包.
随着移动互联网技术的进步,大量的Android应用都会使用分辨率较高的图片,这些图片大多以位图(Bitmap)的形式出现.位图是由称作像素的单个点组成,图像数据采用非压缩格式,加载到内存时需要占用较大的存储空间.当Android应用加载大量图片到内存时,则会有很大概率导致应用内存溢出[4].如果工程师对Android的内存管理机制不了解,很容易造成系统的OOM(Out Of Memory)错误,即内存溢出错误.因此,优化Bitmap的使用方法是开发Android应用程序中比较重要的内容.
对于一些大量使用Bitmap的项目,影响性能的瓶颈主要是Android系统内存管理机制默认分配给应用的堆内存不足.对于Android平台,其托管层使用的Dalvik Java VM,还有很多地方可以进行优化处理.堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的.堆的大小不是一成不变的,当堆内存的实际利用率偏离设定值的时候,虚拟机会在垃圾回收(GC)的时候调整堆内存的大小,让实际占用率向设定值靠拢.在开发一些大型游戏或耗资源的应用中可以考虑手动干涉GC处理,使用dalvik.system.VMRuntime类提供的setTargetHeapUtilization方法可以增强程序堆内存的处理效率,可以在应用onCreate时通过VMRuntime.getRuntime().setTargetHeapUtilization(0.75),把堆内存的利用率设置为75%,优化Dalvik虚拟机的堆内存分配.除了优化Dalvik虚拟机的堆内存分配外,还可以使用Dalvik提供的dalvik.system.VMRuntime类重新定义堆内存的大小,把最小堆内存设置成16 MB.此方法的缺点是,重新设置堆内存涉及内存拷贝,反复更改堆内存的大小势必影响程序效率.
2.2.1 以最省内存的方式读取本地资源的图片
Android应用在加载图片时的颜色模式有4种,分别是①ALPHA_8,每像素占用1 byte内存;②ARGB_4444,每像素占用2 byte内存;③ARGB_8888,每像素占用4 byte内存;④RGB_565,每像素占用2 byte内存.
Android默认的颜色模式为ARGB_8888,这个颜色模式色彩最细腻、显示质量最高,但占用的内存也最大.通过指定加载图片时的颜色模式,可以达到节省内存的目的,代码如下:
以上代码实现了图片资源以RGB_565模式读取.对于大多数图片,RGB_565(或ARGB_4444)模式与ARGB_8888模式的显示效果差别不大.此方法的缺点是,由于采用了显示质量一般的颜色模式加载图片,所以对于渐变效果的图片可能会出现颜色条.此外,使用RGB_565(或ARGB_4444)模式时会影响图片的特效处理.
2.2.2 将图片转化为缩略图加载
如果图片的像素过大,在使用BitmapFactory类方法实例化Bitmap的过程中,需要使用的内存空间可能超过Android系统分配的空间,从而导致内存溢出.如果遇到这种情况,可以在实例化Bitmap时将图片缩小,减少图片载入过程中的内存占用,避免异常发生.
在Android应用中,使用BitmapFactory.Options设置inSampleSize属性可以对图片进行二次采样,属性值inSampleSize表示缩略图的宽度和高度为完整图片的几分之一,代码如下:
以上代码实现了读取缩略图的功能,由于inSampleSize的值为2,则缩略图的宽度和高度都是原始图片的1/2,图片的大小为原始大小的1/4.这种做法的弊病是图片质量会变差,inSampleSize的值越大,图片的质量就越差.在实际项目中,正确的做法是先获取图片真实的宽度和高度,然后判断是否需要使用缩略图.如果不需要,设置inSampleSize的值为1;如果需要,则动态计算并设置inSampleSize的值,对图片进行缩小.
2.3.1 捕获程序异常
Android应用中实例化Bitmap会占用大量内存,为了避免应用在分配Bitmap内存时出现内存溢出异常导致的应用异常结束,需要特别注意实例化Bitmap部分的代码.通常在实例化Bitmap的代码中,一定要对内存溢出异常进行捕获,代码如下:
这段代码通过在初始化Bitmap对象过程中对可能发生的内存溢出异常进行了捕获和处理,即使出现内存溢出的情况,应用也不会崩溃,而是得到一个默认的Bitmap位图.需要注意的是,很多开发者会习惯性地在代码中直接捕获Exception,但是对于Out Of Memory Error来说,这样做的结果是捕获不到异常的,因为Out Of Memory Error是一种Error,而不是Exception.
2.3.2 及时回收内存
由于Bitmap对象占用的内存大,在确认不再使用该图片对象时,可以通过调用Bitmap对象的recycle()方法及时回收内存,有效降低图片本地数据的峰值,减少内存溢出的概率.
recycle()方法的作用是标记图片对象,方便回收图片对象的本地数据,及时释放占用内存,并非真正降低Bitmap使用内存.使用了recycle()方法的图片对象处于“废弃”状态,再次调用时会造成程序错误,所以在无法保证该图片对象绝对不会被再次调用的情况下,不建议使用该方法.特别要注意的是,已经用setImageBitmap()方法分配给控件的图片对象,可能会被系统类库调用,造成程序错误.
2.3.3 缓存通用的Bitmap对象
在Android应用中,经常需要在一个Activity里多次用到同一图片.例如,在一个Activity中展示一些用户的头像列表,如果某用户没有设置头像,则会显示一个默认头像.遇到此类应用时,如果不进行缓存,而采用BitmapFactory类的方法实例化每个Bitmap,尽管看到的是同一图片,得到的却是不同的Bitmap对象.正确的做法是对同一Bitmap对象(默认头像)进行缓存,避免重复新建,以减少内存占用.
有限的内存容量和对内存不断提升的需求始终是矛盾的,Android应用不可避免地会使用图片资源,在处理大量Bitmap数据集时避免内存的溢出有着十分重要的意义.本研究给出了相应的解决方法,通过使用这些方法,在软件设计时充分考虑需求并合理地利用技术有效地管理Android应用的内存,避免应用运行时内存溢出.
[1]余志龙,陈昱勋,郑名杰,等.Google Android SDK开发范例大全[M].北京:人民邮电出版社,2010.
[2]姚昱曼,刘卫国.Android与J2ME平台间即时通信的研究与实现[J].计算机系统应用,2008(12):118-120,127.
[3]黄伟敏.基于XMPP协议的Android即时通信系统设计[J].电子设计工程,2011(19):57-59.
[4]董铖.针对Android应用中Gallery内存溢出的解决方案[D].上海:东华大学,2012.