喜糖

移动开发工程师 。涉及 android、ios、jni

导航

针对图片性能优化的总结

Posted on 2011-09-18 03:14  喜糖  阅读(930)  评论(0编辑  收藏  举报

由于android设备的RAM较小,且Java的GC机制不够智能,经常会出现Out of memory异常。 当然,除了上面的原因,也有可能就是因为写的程序有bug,产生内存溢出。在进行图片较多的软件处理时,很有可能会遇到OOM(out of memory)的异常。

图片是一个非常消耗内存的资源,针对图片的处理需要进行特殊的处理。经过一段时间的调研,我总结出来几个注意事项。

  1.  尽量使用9png格式的图片
  2.  加载图片时,压缩图片后加载
  3.  尽快的手动标记回收图片资源
  4.  设置dalivk虚拟机的初始堆内存大小和GC效率(适用于不单单是图片的问题)
  5.  调用system.gc来执行垃圾回收(不赞成的方法)

下面是对这几个内容的仔细分析:
  • 尽量使用9png格式的图片
    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,
04              maxNumOfPixels);
05  
06      int roundedSize;
07      if (initialSize <=  8 ) {
08          roundedSize =  1 ;
09          while (roundedSize < initialSize) {
10              roundedSize <<=  1 ;
11          }
12      }  else {
13          roundedSize = (initialSize +  7 ) /  8 *  8 ;
14      }
15  
16      return roundedSize;
17 }
18  
19 private static int computeInitialSampleSize(BitmapFactory.Options options,
20          int minSideLength,  int maxNumOfPixels) {
21      double w = options.outWidth;
22      double h = options.outHeight;
23  
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));
29  
30      if (upperBound < lowerBound) {
31          // return the larger one when there is no overlapping zone.
32          return lowerBound;
33      }
34  
35      if ((maxNumOfPixels == - 1 ) &&
36              (minSideLength == - 1 )) {
37          return 1 ;
38      }  else if (minSideLength == - 1 ) {
39          return lowerBound;
40      }  else {
41          return upperBound;
42      }
43 }  

     使用该算法,就可动态计算出图片的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来执行垃圾回收(不赞成的方法)
    在以上功能均不能满足条件时,考虑使用该方法。