解码大位图

时间:2017-09-01 05:09:30

标签: android

我正在开发一个解码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解码为位图,那么一切都可以正常工作。这些应用程序需要更多时间才能显示图像,但图像会在屏幕上加载和绘制。

我试图了解与其他应用相比,我可能做错了什么和不同的做法?

2 个答案:

答案 0 :(得分:0)

首先,您需要设置

 android:largeHeap="true"
在Android清单中

。 然后,在系统库中显示相同大小的图像是错觉。 实际上,每个图书馆或图库都会根据屏幕上的可用尺寸调整图像大小,保持图像的宽高比和质量。

因此,您需要在从系统中获取大图像之前调整图像大小。

高效加载大位图

读取位图尺寸和类型

BitmapFactory类提供了几种解码方法(decodeByteArray()decodeFile()decodeResource()等),用于从各种来源创建位图。根据图像数据源选择最合适的解码方法。这些方法尝试为构造的位图分配内存,因此很容易导致OutOfMemory异常。每种类型的解码方法都有其他签名,可让您通过BitmapFactory.Options类指定解码选项。在解码时将inJustDecodeBounds属性设置为true可避免内存分配,为位图对象返回null,但设置outWidthoutHeightoutMimeType。此技术允许您在构造(和内存分配)位图之前读取图像数据的尺寸和类型。

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组件的尺寸。 屏幕尺寸和当前设备的密度。

See Link

答案 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));

您可以查看 Solve java.lang.OutOfMemoryError trouble in Android