最近简单做了一个自定义相机,拍拍照后将加水印显示在拍照界面的右下角,类似于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.decodeResource和BitmapFactory.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对象及时释放掉。
我的这个问题的解决主要是通过第二中办法,其他办法都试过但都失败了,只是拍的次数多少的问题。
在开发自定义相机应用时遇到OutOfMemory错误,特别是在图像旋转和显示水印时。Android系统默认每个APK最大内存为16MB,当超过此限制时会出现OOM。博客讨论了BitmapFactory.decodeResource和BitmapFactory.decodeStream的源码,指出两者最终调用相同的方法。解决OOM的策略包括:压缩图片、减小Bitmap尺寸(通过设置insampleSize)和及时释放不再使用的Bitmap对象。博主通过调整insampleSize成功解决了问题。

1045

被折叠的 条评论
为什么被折叠?



