由于android设备的RAM较小,且Java的GC机制不够智能,经常会出现Out of memory异常。 当然,除了上面的原因,也有可能就是因为写的程序有bug,产生内存溢出。在进行图片较多的软件处理时,很有可能会遇到OOM(out of memory)的异常。
图片是一个非常消耗内存的资源,针对图片的处理需要进行特殊的处理。经过一段时间的调研,我总结出来几个注意事项。
- 尽量使用9png格式的图片
- 加载图片时,压缩图片后加载
- 尽快的手动标记回收图片资源
- 设置dalivk虚拟机的初始堆内存大小和GC效率(适用于不单单是图片的问题)
- 调用system.gc来执行垃圾回收(不赞成的方法)
下面是对这几个内容的仔细分析:
android系统为了提高图片的质量,提高android对各种屏幕的适应能力。系统推出了对.9.png格式图片的支持。这就就可以把一些图片在美工那里就做的非常小,之后还满足了产品对品质的要求。
正常程序中加载图片调用函数:BitmapFactory.decodeFile(imageFile);
为了在加载图片时可以对图片压缩后在加载,对前面的这段代码进行一下加工:
1 |
BitmapFactory.Options opts = new BitmapFactory.Options(); |
2 |
opts.inJustDecodeBounds = true ; |
3 |
Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts); |
设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度,即opts.width和opts.height。有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize。
查看Android源码,Android提供了一种动态计算的方法。
01 |
public static int computeSampleSize(BitmapFactory.Options options, |
02 |
int minSideLength, int maxNumOfPixels) { |
03 |
int initialSize = computeInitialSampleSize(options, minSideLength, |
07 |
if (initialSize <= 8 ) { |
09 |
while (roundedSize < initialSize) { |
13 |
roundedSize = (initialSize + 7 ) / 8 * 8 ; |
19 |
private static int computeInitialSampleSize(BitmapFactory.Options options, |
20 |
int minSideLength, int maxNumOfPixels) { |
21 |
double w = options.outWidth; |
22 |
double h = options.outHeight; |
24 |
int lowerBound = (maxNumOfPixels == - 1 ) ? 1 : |
25 |
( int ) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); |
26 |
int upperBound = (minSideLength == - 1 ) ? 128 : |
27 |
( int ) Math.min(Math.floor(w / minSideLength), |
28 |
Math.floor(h / minSideLength)); |
30 |
if (upperBound < lowerBound) { |
35 |
if ((maxNumOfPixels == - 1 ) && |
36 |
(minSideLength == - 1 )) { |
38 |
} else if (minSideLength == - 1 ) { |
使用该算法,就可动态计算出图片的inSampleSize。然后去设置好inSampleSize后才去加载图片。
//解决加载图片 内存溢出的问题
//Options 只保存图片尺寸大小,不保存图片到内存
BitmapFactory.Options opts = new BitmapFactory.Options();
//缩放的比例,缩放是很难按准备的比例进行缩放的,其值表明缩放的倍数,SDK中建议其值是2的指数值,值越大会导致图片不清晰
opts.inSampleSize = 4;
Bitmap bmp = null;
bmp = BitmapFactory.decodeResource(getResources(), mImageIds[position],opts);
在图片资源用完时,尽量手动的去标记一下这些已经用完的图片。帮助GC去识别不用的图片,并回收。
if(bitmapObject.isRecycled()==false) //如果没有回收
bitmapObject.recycle();
- 设置dalivk虚拟机的初始堆内存大小和GC效率(适用于不单单是图片的问题)
对于Android平台来说,其托管层使用的Dalvik Java VM从目前的表现来看还有很多地方可以优化处理,比如我们在开发一些大型游戏或耗资源的应用中可能考虑手动干涉GC处理,使用 dalvik.system.VMRuntime类提供的setTargetHeapUtilization方法可以增强程序堆内存的处理效率。当然具体 原理我们可以参考开源工程,这里我们仅说下使用方法: private final static float TARGET_HEAP_UTILIZATION = 0.75f; 在程序onCreate时就可以调用VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION); 即可。
对于一些Android项目,影响性能瓶颈的主要是Android自己内存管理机制问题,目前手机厂商对RAM都比较吝啬,对于软件的流畅性来说RAM对 性能的影响十分敏感,除了 优化Dalvik虚拟机的堆内存分配外,我们还可以强制定义自己软件的对内存大小,我们使用Dalvik提供的 dalvik.system.VMRuntime类来设置最小堆内存为例:
private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ;
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); //设置最小heap内存为6MB大小。当然对于内存吃紧来说还可以通过手动干涉GC去处理
- 调用system.gc来执行垃圾回收(不赞成的方法)
在以上功能均不能满足条件时,考虑使用该方法。