Android - 创建位图时发生内存不足异常

17
我第二次创建位图后遇到了以下错误:
04-17 18:28:09.310: ERROR/AndroidRuntime(3458): java.lang.OutOfMemoryError: bitmap size exceeds VM budget

this._profileBitmap = Bitmap.createBitmap(_profileBitmap, xCoor,  yCoor, width, height);

来自日志:

04-17 18:27:57.500: INFO/CameraCropView(3458): Original Photo Size: W 1536 x H 2048   
04-17 18:28:06.170: INFO/CameraCropView(3458): xCoor: 291   
04-17 18:28:06.170: INFO/CameraCropView(3458): yCoor: 430    
04-17 18:28:06.170: INFO/CameraCropView(3458): Width: 952  
04-17 18:28:06.170: INFO/CameraCropView(3458): Height: 952  

因为图片太大,所以我遇到了错误。但有趣的是,这个错误不是第一次出现,只有当我第二次拍照时才会出现,这让我相信这个profileBitmap没有被销毁。我该如何清理它?


1
你很可能是对的,你没有释放资源。你需要向我们展示一些代码,以便我们能够帮助你... - Chris Thompson
阅读此博客文章:http://codingaffairs.blogspot.com/2016/07/processing-bitmap-and-memory-management.html - Developine
9个回答

16

我曾经遇到同样的问题并通过以下方式解决:

我的应用程序大小约为18MB,当我看到剩余多少内存时我感到震惊——只有654KB(在1GB RAM上!)。所以我只是从项目中删除了几乎所有的图片,并在第一次启动时从互联网上下载它们,在需要时使用来自SD卡的图片。

要检查您的应用程序的总内存/可用内存,请使用:

 Runtime.getRuntime().totalMemory();
 Runtime.getRuntime().freeMemory();

编辑:我忘了最重要的一件事——在应用程序标签之间添加以下行:

android:largeHeap="true"


2
如果你很少遇到内存错误,那么这段代码可以正常工作。android:largeHeap="true"并不能使你的图片加载更加高效,但是它可以提供更多的内存空间。 - Pelanes
1
值得注意的是,增加堆大小也会增加垃圾回收时间,因此您可能会在某些设备上体验到较低的性能。 - TheIT
3
使用大堆只是推迟问题,如果可能的话,首选的方法是重新思考解码过程、删除未使用的资源等。 - milosmns

12

在Android上,使用位图存在许多内存异常问题,其中许多问题已经在stackoverflow上得到讨论。如果可能的话,最好先查看现有的问题,看看你的问题是否与其中之一相同,如果不是,请描述出不同之处。

以下是一些例子:

由于大型位图尺寸而导致的内存不足异常

Gallery中的Android内存不足异常

Android在图像处理时处理内存不足异常

等等: https://stackoverflow.com/search?q=android+out+of+memory+exception+bitmap


7
我已经在这篇博客文章中解释了: android位图处理技巧 现在这里有一些提示,可以遵循并避免您的Android应用程序中的内存不足异常。
  1. 始终使用Activity上下文而不是Application上下文,因为Application上下文无法进行垃圾回收。并在Activity完成时释放资源(对象的生命周期应与活动相同)。

2. 当Activity完成时,请检查HEAP DUMP(Android Studio中的内存分析工具)。

如果HEAP DUMP中有已完成的Activity对象,则存在内存泄漏。请检查代码并确定是什么导致了内存泄漏。

  1. 始终使用inSampleSize。

那么inSampleSize是什么? 通过inSampleSize,您实际上告诉解码器不要在内存中抓取每个像素,而是子采样图像。 这将导致加载到内存中的像素比原始图像少。您可以告诉解码器从原始图像中获取每4个像素或每个第二个像素。 如果inSampleSize为4,则解码器将返回一个图像,其大小为原始图像中像素数量的1/16。

那么您节省了多少内存? 计算一下 :)

  1. 在将位图加载到内存之前读取位图尺寸。

    在将图像加载到内存之前读取位图尺寸如何帮助您避免内存不足错误? 让我们学习一下

    使用inJustBounds = true

这里有一个技巧,可以在将图像加载到内存之前获取其尺寸。

 BitmapFactory.Options options = new BitmapFactory.Options();
 options.inJustDecodeBounds = true;
 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.id.myimage,    options);
 int imageHeight = options.outHeight;
 int imageWidth = options.outWidth;
 String imageType = options.outMimeType;

上面的代码片段将不会给我们任何图像/位图。它将为位图对象返回null。但它肯定会返回那个图像的宽度和高度,即R.id.myimage。 现在你拥有了图像的宽度和高度。你可以根据以下因素缩放图像: 1. 显示图像的ImageView的大小。 2. 可用的内存量。您可以使用ActivityManager和getMemoryClass来检查可用的内存量。 3. 设备的屏幕大小和密度。 4. 使用适当的位图配置。位图配置是图像的颜色空间/颜色深度。在Android中,默认的位图配置是RGB_8888,即每个像素4个字节。 5. 如果使用RGB_565颜色通道,则每像素使用2个字节。对于相同的分辨率减少了一半的内存分配 :) 6. 使用inBitmap属性进行回收。 7. 不要将静态Drawable对象作为它无法被垃圾回收。 8. 在清单文件中请求大堆。 9. 如果您正在进行大量的图像处理(内存密集型任务),则使用多个进程或使用NDK(使用C、C++进行本地开发)。

5

当您使用完位图后,可以尝试调用recycle()方法。这将清除所有图像数据并释放内存。如果此后有任何尝试绘制位图的操作,则您的应用程序将崩溃。如果发生崩溃,可能会帮助您找出仍在持有位图的内容。


5

3

对于较大的图像,可以通过将它们采样成较小的尺寸来避免这种情况。请参考以下示例 -

    File f = new File(selectedImagePath); 

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(new FileInputStream(f), null, options); 
    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, 720, 1280); //My device pixel resolution
    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;    
    Bitmap bmpPic = BitmapFactory.decodeStream(new FileInputStream(f), null, options); 


    Bitmap bmpPic1 = Bitmap.createBitmap(bmpPic, 0, 0, bmpPic.getWidth(), bmpPic.getHeight(), mat, true);   
    img.setImageBitmap(bmpPic1);  //img is your ImageView

Reference- http://developer.android.com/training/displaying-bitmaps/load-bitmap.html


这仅涵盖了加载位图,假设我需要将位图写入存储,现在使用createBitmap创建位图会出错... - Shenanigans1

3
你可以使用矢量Drawable,它使用xml文件来描述图像,因此占用较少的内存。要做到这一点,您应该使用SVG格式的图像,然后使用以下两种解决方案之一生成xml文件:
1.解决方案1:在Android Studio中使用矢量资源工作室:右键单击项目中的Drawable文件->新建->向量资产 2.解决方案2:使用svg2android网站:https://inloop.github.io/svg2android 请查看此链接以获取更多信息:https://developer.android.com/studio/write/vector-asset-studio.html

1
我之前遇到了这个问题,是因为我先修改了一张位图,接着再次对这个已经修改过的版本进行二次修改。这样就会导致同一张位图的三个版本(原始版和两个修改版)同时存在于内存中。
我解决了这个问题,通过对我的图像编辑代码进行修改,将两个修改同时应用于同一张位图,相当于批量处理,减少了我应用程序需要在内存中保存的修改版数量。

1

当手机关机并重新开机时,我遇到了同样的问题。只需将位图设置为null并调用System.gc(); 就可以解决所有问题。


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