android优化(二)---App调试内存泄露之图片Bitmap篇

android优化(二)---App调试内存泄露之图片Bitmap篇


android优化中,做到裁减应用资源,不仅仅能够减少apk 的大小,同时有效的避免一些内存泄露,做到应用优化。

a 尽量少使用图片

客户在设计应用显示效果时候,为了达到界面美化的目的。很多地方会使用图片,有的应用整个的全屏背景都使用图片。图片的大量使用不仅会是的应用的APK的大小增大,而且会增大应用显示的内存。

在设计界面的时候,请尽量多用颜色或者9png图片。如设置一个纯色的图片作为背景,此时可以用颜色代替。

ps:优先使用颜色,不行再用9png,最后考虑使用图片。

 

b 高效的使用Bitmap  

1).压缩Bitmap

  如果图片像素过大,使用BitmapFactory类的方法实例化Bitmap的过程中,需要大于8M的内存空间,就必定会发生OutOfMemory异常。这个时候该如何处理呢?如果有这种情况,则可以将图片缩小,以减少载入图片过程中的内存的使用,避免异常发生。

       BitmapFactory提供了几种解码方式(decodeByteArray(),decodeFile(), decodeResource()等等),以便从多种资源中创建一个Bitmap(位图)对象。可以根据你的图片数据来源选择最合适的解码方式。每一种解码方式都有额外的特征,你可以通过BitmapFactory.Options类指定解码方法。

1

BitmapFactory.Options options = new BitmapFactory.Options();

2

options.inJustDecodeBounds = true;

3

BitmapFactory.decodeResource(getResources(), R.id.myimage, options);

4

int imageHeight = options.outHeight;

5

int imageWidth = options.outWidth;

6

String imageType = options.outMimeType;

 // 打印出图片的宽和高

 Log.d("example", opts.outWidth + "," + opts.outHeight);

---在解码图片之前就要检查图片的尺寸。设置inJustDecodeBounds属性为true,可以避免内存分配,返回的bitmap对象为null却可以设置outWidth, outHeight和outMimeType。这项技术允许你在创建Bitmap(并分配内存)之前读取图片的尺寸和类型。除非你十分确信图片资源的尺寸是可预见的并且有着充裕的可用内存。

---使用BitmapFactory.Options设置inSampleSize就可以缩小图片。属性值inSampleSize表示缩略图大小为原始图片大小的几分之一。即如果这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片的大小就为原始大小的1/4。

---在实际项目中,可以利用上面的代码,先获取图片真实的宽度和高度,然后判断是否需要跑缩小。如果不需要缩小,设置inSampleSize的值为1。如果需要缩小,则动态计算并设置inSampleSize的值,对图片进行缩小。需要注意的是,在下次使用BitmapFactory的decodeFile()等方法实例化Bitmap对象前,别忘记将opts.inJustDecodeBound设置回false。否则获取的bitmap对象还是null。

经验分享:

如果程序的图片的来源都是程序包中的资源,或者是自己服务器上的图片,图片的大小是开发者可以调整的,那么一般来说,就只需要注意使用的图片不要过大,并且注意代码的质量,及时回收Bitmap对象,就能避免OutOfMemory异常的发生。

如果程序的图片来自外界,这个时候就特别需要注意OutOfMemory的发生。一个是如果载入的图片比较大,就需要先缩小;另一个是一定要捕获异常,避免程序Crash。

 

2) 缓存Bitmap

加载一个Bitmap(位图)到你的UI界面是非常简单的,但是如果你要一次加载一大批,事情就变得复杂多了。在大多数的情况下(如ListView、GridView或者ViewPager这样的组件),屏幕上的图片以及马上要在滚动到屏幕上显示的图片的总量,在本质上是不受限制的。 像这样的组件在子视图移出屏幕后会进行视图回收,内存使用仍被保留。但假设你不保留任何长期存活的引用,垃圾回收器也会释放你所加载的Bitmap。这自然再好不过了,但是为了保持流畅且快速加载的UI,你要避免继续在图片回到屏幕上的时候重新处理。使用内存和硬盘缓存通常能解决这个问题,使用缓存允许组件快速加载并处理图片。

使用内存缓存,  以牺牲宝贵的应用内存为代价,内存缓存提供了快速的Bitmap访问方式。LruCache类(可以在Support Library中获取并支持到API  Level 4以上,即1.6版本以上)是非常适合用作缓存Bitmap任务的,它将最近被引用到的对象存储在一个强引用的LinkedHashMap中,并且在缓存超过了指定大小之后将最近不常使用的对象释放掉。

 注意:以前有一个非常流行的内存缓存实现是SoftReference(软引用)或者WeakReference(弱引用)的Bitmap缓存方案,然而现在已经不推荐使用了。自Android2.3版本(API Level 9)开始,垃圾回收器更着重于对软/弱引用的回收,这使得上述的方案相当无效。此外,Android 3.0(API Level 11)之前的版本中,Bitmap的备份数据直接存储在本地内存中并以一种不可预测的方式从内存中释放,很可能短暂性的引起程序超出内存限制而崩溃。

 

3)及时回收Bitmap

在Android应用里,最耗费内存的就是图片资源。垃圾的回收是靠系统来调度的,但是网上说Bitmap 的内存自动回收机制不是那么理想。如果一个Bitmap 对象比较占内存,当它不在被使用的时候,最好可以调用Bitmap.recycle()方法主动进行回收。

// 先判断是否已经回收
if(bitmap != null && !bitmap.isRecycled()){ 
        // 回收并且置为null
        bitmap.recycle(); 
        bitmap = null; 
} 
System.gc();

从上面的代码可以看到,bitmap.recycle()方法用于回收该Bitmap所占用的内存,接着将bitmap置空,最后使用System.gc()调用一下系统的垃圾回收器进行回收,可以通知垃圾回收器尽快进行回收。这里需要注意的是,调用System.gc()并不能保证立即开始进行回收过程,而只是为了加快回收的到来。

如何调用recycle()方法进行回收已经了解了,那什么时候释放Bitmap的内存比较合适呢?一般来说,如果代码已经不再需要使用Bitmap对象了,就可以释放了。释放内存以后,就不能再使用该Bitmap对象了,如果再次使用,就会抛出异常。所以一定要保证不再使用的时候释放。比如,如果是在某个Activity中使用Bitmap,就可以在Activity的onStop()或者onDestroy()方法中进行回收。


4) 捕获异常

因为Bitmap是吃内存大户,为了避免应用在分配Bitmap内存的时候出现OutOfMemory异常以后Crash掉,需要特别注意实例化Bitmap部分的代码。通常,在实例化Bitmap的代码中,一定要对OutOfMemory异常进行捕获。

Bitmap bitmap = null;
try {
    // 实例化Bitmap
    bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError e) {
    //
}
if (bitmap == null) {
    // 如果实例化失败 返回默认的Bitmap对象
    return defaultBitmapMap;
}
这里对初始化Bitmap对象过程中可能发生的OutOfMemory异常进行了捕获。如果发生了OutOfMemory异常,应用不会崩溃,而是得到了一个默认的Bitmap图。

经验分享:

    很多开发者会习惯性的在代码中直接捕获Exception。但是对于OutOfMemoryError来说,这样做是捕获不到的。因为OutOfMemoryError是一种Error,而不是Exception。在此仅仅做一下提醒,避免写错代码而捕获不到OutOfMemoryError。





你可能感兴趣的:(【android,代码优化】)