68

我的应用程序在源代码的以下行中遇到 OOM 错误:

image = BitmapFactory.decodeStream(assetManager.open(imgFilename));

就在导致应用程序因 OOM 错误而被终止的分配之前:

(...)
08-05 21:22:12.443: I/dalvikvm-heap(2319): Clamp target GC heap from 25.056MB to 24.000MB
08-05 21:22:12.443: D/dalvikvm(2319): GC_FOR_MALLOC freed <1K, 50% free 2709K/5379K, external 18296K/19336K, paused 58ms
08-05 21:22:14.513: D/dalvikvm(2319): GC_EXTERNAL_ALLOC freed <1K, 50% free 2709K/5379K, external 18296K/19336K, paused 101ms
08-05 21:22:14.903: I/dalvikvm-heap(2319): Clamp target GC heap from 25.073MB to 24.000MB
08-05 21:22:14.903: D/dalvikvm(2319): GC_FOR_MALLOC freed 0K, 50% free 2709K/5379K, external 18312K/19336K, paused 53ms
08-05 21:22:22.843: D/ddm-heap(2319): Heap GC request
08-05 21:22:22.963: I/dalvikvm-heap(2319): Clamp target GC heap from 25.073MB to 24.000MB
08-05 21:22:22.963: D/dalvikvm(2319): threadid=1: still suspended after undo (sc=1 dc=1)
08-05 21:22:22.963: D/dalvikvm(2319): GC_EXPLICIT freed 1K, 50% free 2710K/5379K, external 18312K/19336K, paused 116ms

DDMS 报告了关于堆状态的类似情况:

Heap Size:  5.254 MB
Allocated:  2.647 MB
Free:   2.607 MB
%Used:  50.38%
#Objects    49,028  

单步越过这条线会导致 OOM 错误:

08-05 21:26:04.783: D/dalvikvm(2319): GC_EXTERNAL_ALLOC freed <1K, 50% free 2710K/5379K, external 18312K/19336K, paused 57ms
08-05 21:26:05.023: E/dalvikvm-heap(2319): 2097152-byte external allocation too large for this process.
08-05 21:26:05.163: I/dalvikvm-heap(2319): Clamp target GC heap from 25.073MB to 24.000MB
08-05 21:26:05.163: E/GraphicsJNI(2319): VM won't let us allocate 2097152 bytes
08-05 21:26:05.163: D/dalvikvm(2319): GC_FOR_MALLOC freed 0K, 50% free 2710K/5379K, external 18312K/19336K, paused 30ms
08-05 21:26:05.283: D/skia(2319): --- decoder->decode returned false
  1. 据报道,“imgFileName”引用的文件大小在 Windows 上小于 400K。那么为什么 BitmapFactory.decodeStream 会尝试分配 2MB 呢?
  2. 当似乎有足够的可用空间时,为什么会出现 OOM 错误?

此应用程序面向 Android 2.2 及更高版本。

提前致谢!

4

8 回答 8

91

Android 库在加载图像方面并不那么聪明,因此您必须为此创建解决方法。

在我的测试中,Drawable.createFromStream使用的内存比BitmapFactory.decodeStream.

您可以更改配色方案以减少内存(RGB_565),但图像也会降低质量:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);

参考:http: //developer.android.com/reference/android/graphics/Bitmap.Config.html

您还可以加载缩放的图像,这将大大减少内存使用量,但您必须知道您的图像不会失去太多的质量。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);

参考:http: //developer.android.com/reference/android/graphics/BitmapFactory.Options.html

要动态定义 inSampleSize,您可能想知道图像大小以做出决定:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
bitmap = BitmapFactory.decodeStream(stream, null, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;

options.inJustDecodeBounds = false;
// recreate the stream
// make some calculation to define inSampleSize
options.inSampleSize = ?;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);

您可以根据设备的屏幕尺寸自定义 inSampleSize。要获取屏幕大小,您可以执行以下操作:

DisplayMetrics metrics = new DisplayMetrics();
((Activity) activity).getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight =metrics.heightPixels;

其他教程: - http://developer.android.com/training/displaying-bitmaps/load-bitmap.html - http://developer.android.com/training/displaying-bitmaps/index.html

于 2013-05-13T18:13:22.873 回答
42

有关更有效地加载大型位图的指南,请参阅此:

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

一个 400 KB 的图像文件很容易占用 5-10 MB 的 RAM。

于 2012-08-05T21:47:59.377 回答
8

磁盘上文件的大小不一定与内存中文件的大小一致。文件很可能是压缩的,而解码时则不会。您需要在计算中考虑到这一点。

将图像的大小(宽 x 高)乘以图像的颜色深度,得到图像的内存大小。

于 2012-08-05T21:46:46.250 回答
3

Basically you can resolve your issue by trying to scale your Bitmap and you'll see memory consumption reduced. To do it you can copy he method shown here.

Also, there is a dedicated page at Android Developeres that could help you understand better how to load large Bitmaps. Take a look at the official documentation.

于 2012-08-05T22:52:26.800 回答
1

虽然上述答案显然是正确的,但更好的做法是在不再使用ImageView 的 bitmap/src 属性时将其显式设置为null ,主要是在您的活动被破坏时。任何其他重型资源(大文本、音频、视频)等也可能被取消。这确保了资源被立即释放,而不是等待 GC 收集。

于 2014-01-04T12:13:47.047 回答
0

您可以尝试通过Glide库分配位图。Glide 使用最合适的方法(特定于您的设备和环境)将位图文件移植到图形层(ImageView)中。

好,易于:

Glide.with(getContext())
            .load(imageFile.getPath())
            .into(previewImageView);
于 2020-04-21T21:10:45.077 回答
-2

我使用以下方法解决了 OutOfMemoryError问题

private Bitmap downloadImageBitmap(String sUrl) {
        this.url = sUrl;
        bitmap = null;
                    try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            for (options.inSampleSize = 1; options.inSampleSize <= 32; options.inSampleSize++) {
                InputStream inputStream = new URL(sUrl).openStream();  
                try {
                    bitmap = BitmapFactory.decodeStream(inputStream, null, options);      
                    inputStream.close();
                    break;
                } catch (OutOfMemoryError outOfMemoryError) {
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return bitmap;
    }
于 2018-07-09T06:56:02.020 回答
-3

我将 insample 大小更改为 2。我的问题已解决。但请确保图像质量不会受到破坏。

于 2015-09-23T10:05:22.570 回答