如何在进行位图处理时避免“内存不足异常”?

6
onPictureTaken函数中,我想要执行以下操作:
Bitmap decodedPicture = BitmapFactory.decodeByteArray(data, 0, data.length);
Matrix matrix = new Matrix();
matrix.preScale(-1.0f, 1.0f);
Bitmap picture = Bitmap.createBitmap(decodedPicture, 0, 0, decodedPicture.getWidth(), decodedPicture.getHeight(), matrix, false);

View v1 = mainLayout.getRootView();
v1.setDrawingCacheEnabled(true);
Bitmap screenshot = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);

Bitmap scaledPicture = Bitmap.createScaledBitmap(picture, screenshot.getWidth(), screenshot.getHeight(), true);

Bitmap compos = Bitmap.createBitmap(scaledPicture.getWidth(), scaledPicture.getHeight(), Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(compos);
canvas.drawBitmap(scaledPicture, new Matrix(), null);
canvas.drawBitmap(screenshot, new Matrix(), null);

MediaStore.Images.Media.insertImage(getContentResolver(), compos, "name" , "description");
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));

我的唯一要求是想保存高质量的照片……似乎我可能需要做出一些牺牲。

在我的 Nexus 4 和新设备上,这段代码可以正常运行并按预期工作。但在内存较少的旧设备上,我会遇到内存不足的情况!:(

如何在不超过内存限制的情况下进行同样的图像处理?我并不想在屏幕上显示这些图片,因此与缩小图像有关的解决方案并不适用于此处......


你能将一些引用置空,然后提示GC吗?这样可以清除一些不必要的数据... - tilpner
4个回答

3
您需要增加采样率读取位图。技巧在于找到正确的采样率,这样当您最终缩放图像时不会导致分辨率降低。我在这里写了一篇博客文章,其中包括一个用于缩放的实用程序类。

http://zerocredibility.wordpress.com/2011/01/27/android-bitmap-scaling/

根据您的特定需求,您可以大大简化该类。
要点是只读取位图的大小。基于所需缩放大小计算出最佳采样率,使用该采样率读取位图,然后将其微调为您想要的确切大小。

0

你有很多Bitmap对象闲置不用。尝试回收/重复使用其中的一些。

我不确定你的具体需求是什么,但我可以看到你可以通过简单地这样做来节省一些内存。

        Bitmap decodedPicture = BitmapFactory.decodeByteArray(data, 0, data.length);
        Matrix matrix = new Matrix();
        matrix.preScale(-1.0f, 1.0f);
        Bitmap picture = Bitmap.createBitmap(decodedPicture, 0, 0, decodedPicture.getWidth(), decodedPicture.getHeight(), matrix, false);

        decodedPicture.recycle();
        decodedPicture=null;

        View v1 = mainLayout.getRootView();
        v1.setDrawingCacheEnabled(true);
        Bitmap screenshot = Bitmap.createBitmap(v1.getDrawingCache());
        v1.setDrawingCacheEnabled(false);

        Bitmap scaledPicture = Bitmap.createScaledBitmap(picture, screenshot.getWidth(), screenshot.getHeight(), true);

        picture.recycle();
        picture=null;

        Bitmap compos = Bitmap.createBitmap(scaledPicture.getWidth(), scaledPicture.getHeight(), Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(compos);
        canvas.drawBitmap(scaledPicture, new Matrix(), null);
        canvas.drawBitmap(screenshot, new Matrix(), null);

        MediaStore.Images.Media.insertImage(getContentResolver(), compos, "name" , "description");
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));

还要注意内存占用情况,确保所使用的设备内存不会过大。

另外,对于 Android 3.0 及以上版本,位图像素数据是在本地层面上分配的。你需要使用 recycle()finalizer() 来释放内存。



0

考虑到您不想调整位图大小并且不想显示它,我会这样做:

  1. 使用inJustDecodeBounds加载位图,以查看其原始高度和宽度(代码来自这里

    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);
    
    // 计算 inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    
  2. 根据您拥有的大小和内存,您可以直接从那里处理它(即加载位图),或者继续使用Bitmap.createBitmap方法加载一些所述位图的块,该方法允许您仅加载数据块。 可选:考虑将其转换为字节数组(请参见下面的代码)并在处理块之前使用null+ recycle()

代码

ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream();
bitmapPicture.compress(Bitmap.CompressFormat.PNG, COMPRESSION_QUALITY, byteArrayBitmapStream);
byte[] b = byteArrayBitmapStream.toByteArray();

0

我在使用Bitmap类时会用到WeakReference,然后总是在实例对象WeakReference上调用recycle方法。以下是旋转图像的代码片段:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inPurgeable = true;
options.inInputShareable = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
WeakReference<Bitmap> imageBitmapReference = new WeakReference<Bitmap>(BitmapFactory.decodeByteArray(params[0], 0, params[0].length, options));

Matrix mat = new Matrix();
mat.postRotate(90.0f); 
imageBitmapReference = new WeakReference<Bitmap (Bitmap.createBitmap(imageBitmapReference.get(), 0, 0, resolution[0], resolution[1], mat, true));

FileOutputStream fos = new FileOutputStream(filename);
imageBitmapReference.get().compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
imageBitmapReference.get().recycle();

第二个解决方案是如何使用位图而不会出现内存溢出异常,可以使用库Universal Image Loader

(当然,第三个解决方案是在AndroidManifest中设置属性android:largeHeap="true",但真的不要使用这个属性)。

完美的材料在http://developer.android.com/training/displaying-bitmaps/index.html和视频https://www.youtube.com/watch?v=_CruQY55HOk上可以找到。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接