在 Android 应用程序上创建位图时出现 OOM

OOM when creating Bitmaps on an Android application

我有一个应用程序可以从相机预览中获取固定数量的图像并将它们转换成 Bitmaps 的列表。为此,我有以下代码:

private Camera.PreviewCallback SetPreviewCallBack() {
    return new Camera.PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
    List<Bitmap> imageList = new ArrayList<Bitmap>();
    for (int i=0; i<30; i++) // Let's suppose this is the real loop. 
                             // It's not, as the real loop takes each camera preview frame, 
                             // instead of inserting the same one 30 times. 
                             // But for this example, it's OK
    {
        imageList.add(GetBitmap(
                    data, 
                    previewWidth, // Calculated
                    previewHeight, // Calculated 
                    previewFormat, // Calculated
                    previewRotation)); // Calculated
    }

}

private Bitmap GetBitmap(byte[] data, int width, int height, int previewFormat, int rotation) { 

    YuvImage yuv = new YuvImage(data, previewFormat, width, height, null);

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    yuv.compressToJpeg(new Rect(0, 0, width, height), 50, out);

    byte[] bytes = out.toByteArray();
    final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

    Bitmap imageResult = RotateImage(bitmap, 4 - rotation);
    bitmap.recycle();

    return imageResult;
} 

private Bitmap RotateImage(Bitmap rotateImage, int rotation) {

    Matrix matrix = new Matrix();
    switch (rotation) {
    case 1:
        matrix.postRotate(270);
        break;
    case 2:
        matrix.postRotate(180);
        break;
    case 3:
        matrix.postRotate(90);
        break;
    }

    return Bitmap.createBitmap(rotateImage, 0, 0, rotateImage.getWidth(),
            rotateImage.getHeight(), matrix, true);
}

我做的是: - 我将此图像存储在 Singleton class 上,以便从同一应用程序中包含的另一个 Activity 访问它。 - 我再次调用这段代码(当某些事件发生时)并重复图像 extraction/saving 过程。

03-05 09:35:13.339: E/AndroidRuntime(8762): FATAL EXCEPTION: Thread-818
03-05 09:35:13.339: E/AndroidRuntime(8762): java.lang.OutOfMemoryError
03-05 09:35:13.339: E/AndroidRuntime(8762):     at android.graphics.Bitmap.nativeCreate(Native Method)
03-05 09:35:13.339: E/AndroidRuntime(8762):     at android.graphics.Bitmap.createBitmap(Bitmap.java:726)
03-05 09:35:13.339: E/AndroidRuntime(8762):     at android.graphics.Bitmap.createBitmap(Bitmap.java:703)
03-05 09:35:13.339: E/AndroidRuntime(8762):     at android.graphics.Bitmap.createBitmap(Bitmap.java:636)
03-05 09:35:13.339: E/AndroidRuntime(8762):     at com.facephi.sdk.ui.CameraPreview.RotateImage(CameraPreview.java:779)
03-05 09:35:13.339: E/AndroidRuntime(8762):     at com.facephi.sdk.ui.CameraPreview.GetBitmap(CameraPreview.java:712)

我正在尝试使用最佳实践以正确的方式删除未使用的 Bitmaps,如发布的 here,但没有成功...

知道为什么我会遇到这个 OOM 错误吗?

您在 Android 中的内存非常有限,这就是您收到此异常的原因。一般来说,你不需要在内存中存储那么多图像,只应在需要时加载它们(即显示它们或用它们做某事),并应在你使用它们时尽快处理它们 (recycle())完成他们。此外,您应该只以尽可能低的分辨率加载它们。

说了这么多,但不知道为什么必须将它们全部放在内存中(您可能有正当理由),您可以通过在清单中指定适当的属性来增加应用程序的堆大小 ( http://developer.android.com/guide/topics/manifest/application-element.html#largeHeap) 但即便如此也不能保证您可以加载太多图像。

请记住,默认情况下,Android 将为您的图片使用每个像素 4 个字节,因此如果您有 30 张图片,每张图片的像素密度为 100 万,您将使用 1.2 亿字节或大约 120MB的记忆。相比之下,分配给您的应用程序的默认字节可能低至区区 16mb(取决于许多情况。有关更多详细信息,请参见此处:Android heap size on different phones/devices and OS versions)。

如果您能以某种方式更改您的代码,这样您就不必将它们全部加载到内存中,您可以看看位图缓存 (http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html),它可以极大地帮助您进行内存管理问题。

1. 尝试使用

BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 8;
options.inDither = false;
options.inPurgeable = true;
options.inInputShareable = true;
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);

这将大大有助于节省内存。

2. 不要 "store" 你的 Bitmap 对象在单例或其他任何地方。这将始终导致 OutOfMemoryError。每次要使用时重新创建 Bitmap

编辑:

另请参阅官方 ThreadSample example and the Displaying Bitmaps Efficiently 教程。