加载Bitmap对象时出现OutOfMemory

在开发自定义相机应用时遇到OutOfMemory错误,特别是在图像旋转和显示水印时。Android系统默认每个APK最大内存为16MB,当超过此限制时会出现OOM。博客讨论了BitmapFactory.decodeResource和BitmapFactory.decodeStream的源码,指出两者最终调用相同的方法。解决OOM的策略包括:压缩图片、减小Bitmap尺寸(通过设置insampleSize)和及时释放不再使用的Bitmap对象。博主通过调整insampleSize成功解决了问题。

最近简单做了一个自定义相机,拍拍照后将加水印显示在拍照界面的右下角,类似于android的原生相机,但是在拍照及显示的过程中,总会出现

OutOfMemory这个错误,下面总结一个解决这个问题的方法。

先说一下这个Bug出现的场景:由于自定义相机的预览窗口需要进行旋转才能正常预览,但是在获取到拍照后的图片也需要进行旋转,要不然就

和预览窗口未旋转之前的一样,在旋转之后就需要获取水印图片的bitmap对象,然后进行原图和水印图的合成,合成之后将图像保存到sd中,以

及在ImageView中显示出来在上水印时需要重新建立一个Bitmap对象,然后使用Canvas将原图和水印图画上去。如果不进行图像旋转则不会出现

OOM现象,在进行旋转的情况下,将图片显示出来,多拍几张就会出现OOM现象,在图像旋转和图片显示的情况下,在水印图创建时则会出现

OOM现象。也就是在createBitmap这个方法调用时很容易出现OOM现象。

出现OOM现象的原因:由于android dalvik的机制,其默认每单个apk默认的最大内存为16M,如果apk运行时的占用内存超过这个阈值,则会出

现OOM现象。在csdn上看到了一篇博客:android bitmap out of memory总结、心得,我求证了一下,在这里顺带辟谣一下,我看的是

android4.03的源码在这篇博客中所说的BitmapFactory.decodeResourceBitmapFactory.decodeStream最终所调用的方法是不一样的,但

是源码中最终的调用确是一样的

decodeStream中部分的源码如下:

        Bitmap  bm;

        if (is instanceof AssetManager.AssetInputStream) {
            bm = nativeDecodeAsset(((AssetManager.AssetInputStream) is).getAssetInt(),
                    outPadding, opts);
        } else {
            // pass some temp storage down to the native code. 1024 is made up,
            // but should be large enough to avoid too many small calls back
            // into is.read(...) This number is not related to the value passed
            // to mark(...) above.
            byte [] tempStorage = null;
            if (opts != null) tempStorage = opts.inTempStorage;
            if (tempStorage == null) tempStorage = new byte[16 * 1024];
            bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
        }
        if (bm == null && opts != null && opts.inBitmap != null) {
            throw new IllegalArgumentException("Problem decoding into existing bitmap");
        }

可以看出他最终调用的是nativeDecodeAsset这个native方法,那么我们接下来看decodeResource方法

Bitmap bm = null;
        InputStream is = null; 
        
        try {
            final TypedValue value = new TypedValue();
            is = res.openRawResource(id, value);

            bm = decodeResourceStream(res, value, is, null, opts);
        } catch (Exception e) {
            /*  do nothing.
                If the exception happened on open, bm will be null.
                If it happened on close, bm is still valid.
            */
        } finally {
            try {
                if (is != null) is.close();
            } catch (IOException e) {
                // Ignore
            }
        }

可以看出他调用了decodeResourceStream这个方法,那么接下来我们来看这个方法

    public static Bitmap decodeResourceStream(Resources res, TypedValue value,
            InputStream is, Rect pad, Options opts) {

        if (opts == null) {
            opts = new Options();
        }

        if (opts.inDensity == 0 && value != null) {
            final int density = value.density;
            if (density == TypedValue.DENSITY_DEFAULT) {
                opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
            } else if (density != TypedValue.DENSITY_NONE) {
                opts.inDensity = density;
            }
        }
        
        if (opts.inTargetDensity == 0 && res != null) {
            opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
        }
        
        return decodeStream(is, pad, opts);
    }

从这个方法中可以看出,他最终调用的还是decodeStream这个方法,也就是最终调用的都是nativeDecoderAsset这个方法,不存在什么调用java

层的createBitmap这个方法。

解决OOM的主要方法:

一、压缩图片,也就是降低图片质量,但是这个不一定能够起到作用,因为我们获取Bitmap对象是将图片资源进行解码后获取的Bitmap对象,所

以关键是这个解码后的Bitmap对象不能占用太大内存,否则也很容易出现OOM现象。图片压缩可以参考这篇博文:

http://104zz.iteye.com/blog/1694762

二、缩小图片的尺寸,这个非常有用,设置bitmapfactory.options.insamplesize的大小就很容易缩小图片尺寸。具体如何设置这个值可以参考下面

这篇博文java.lang.OutOfMemoryError: bitmap size exceeds VM budget解决方法,简单的设置,在拍照后,获取到字节数组后:

	Options ops = new BitmapFactory.Options();
	ops.inSampleSize = 5;
	Bitmap bitmap1 = BitmapFactory.decodeByteArray(paramArrayOfByte, 0, paramArrayOfByte.length, ops);

这个inSampleSize为多少则表示略缩图的宽,高为原图的几分之几,如为5,则略缩图的宽,高位原图的1/5,图片大小为原图的1/25。这样就很

容易将图片缩小为原图的1/25之一。

三、将不使用的Bitmap对象及时释放掉。

我的这个问题的解决主要是通过第二中办法,其他办法都试过但都失败了,只是拍的次数多少的问题。

参考链接:http://www.cnblogs.com/Soprano/articles/2577152.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值