我正在开发一个解码jpeg文件的android应用程序,并使用canvas.drawBitmap()在画布上绘制位图。我正在使用BitmapFactory.decodeStream()将jpeg解码为位图。
当我处理诸如2560x1536等分辨率的图像时,一切正常。最近,我做了一个压力测试,试图打开一个非常大的5400x3600分辨率的jpeg。我的应用程序因“java.lang.OutOfMemoryError”错误而崩溃。
这是堆栈跟踪:
java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:720)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:696)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:734)
...
这是可以理解的,因为它是一个大图像,解码后的位图使用大量内存。
然而,我想要了解的是在同一部手机上,如果我使用图库应用程序,图库应用程序可以显示相同的大图像而没有任何问题。
在同一部手机上,如果我也尝试使用一些图片编辑工具打开相同的大图像,我认为也应该将jpeg解码为位图,那么一切都可以正常工作。这些应用程序需要更多时间才能显示图像,但图像会在屏幕上加载和绘制。
我试图了解与其他应用相比,我可能做错了什么和不同的做法?
答案 0 :(得分:0)
首先,您需要设置
android:largeHeap="true"
在Android清单中。 然后,在系统库中显示相同大小的图像是错觉。 实际上,每个图书馆或图库都会根据屏幕上的可用尺寸调整图像大小,保持图像的宽高比和质量。
因此,您需要在从系统中获取大图像之前调整图像大小。
高效加载大位图
读取位图尺寸和类型
BitmapFactory类提供了几种解码方法(decodeByteArray()
,decodeFile()
,decodeResource()
等),用于从各种来源创建位图。根据图像数据源选择最合适的解码方法。这些方法尝试为构造的位图分配内存,因此很容易导致OutOfMemory异常。每种类型的解码方法都有其他签名,可让您通过BitmapFactory.Options类指定解码选项。在解码时将inJustDecodeBounds
属性设置为true可避免内存分配,为位图对象返回null,但设置outWidth
,outHeight
和outMimeType
。此技术允许您在构造(和内存分配)位图之前读取图像数据的尺寸和类型。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
为了避免java.lang.OutOfMemory异常,请在解码之前检查位图的尺寸,除非您完全信任该源为您提供可预测大小的图像数据,这些数据可以轻松地放入可用内存中。
将缩小版本加载到内存中 既然图像尺寸已知,它们可用于决定是否应将完整图像加载到内存中,或者是否应加载子采样版本。以下是需要考虑的一些因素:
估计在内存中加载完整图像的内存使用情况。 在给定应用程序的任何其他内存要求的情况下,您愿意承诺加载此映像的内存量。 要加载图像的目标ImageView或UI组件的尺寸。 屏幕尺寸和当前设备的密度。
答案 1 :(得分:0)
<强> OutOfMemoryError 强>
当Java虚拟机无法分配对象时抛出,因为 内存不足,没有更多的内存可供使用 垃圾收集器。
确保,已添加
<application
android:largeHeap="true"
>
要避免 java.lang.OutOfMemory
例外,请在解码之前检查位图的尺寸,除非您完全信任该源为您提供可预测大小的图像数据,这些图像数据可以轻松地放入可用内存中
阅读 Loading Large Bitmaps Efficiently 。
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
此方法可以轻松地将任意大尺寸的位图加载到显示 100x100 像素缩略图的ImageView中,如下面的代码所示:
imgOBJ.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));