Android:BitmapFactory.decodeStream()内存不足,带有400KB文件和2MB可用堆


67

我的应用在源代码的以下行中遇到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. 在Windows上,“ imgFileName”引用的文件的大小据报告为<400K。那么,为什么BitmapFactory.decodeStream尝试分配2MB?
  2. 当似乎有足够的可用空间时,为什么会出现OOM错误?

这个应用程式的目标是Android 2.2以上版本。

提前致谢!

Answers:


89

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


1
如何计算图像大小以动态计算inSampleSize?
NinjaCoder

1
我已经编辑了问题,包括您问题的答案!
Paulo Check

1
屏幕尺寸不是图像尺寸。我有一个允许用户拍摄或选择照片的应用程序。拍照可能会使用设备的完整相机分辨率或更低,具体取决于设置。选择照片的大小可以是任何大小,由于Picassa的存储等原因,它不必是本地的。因此,我的应用会返回一个URI。无法知道该URI的内容,也无法知道其大小。那我将采样大小设置为什么呢?我无法相信没有实现这一目标的好方法。
保罗

更改为RGB_55并缩放图像对我来说很成功。希望此错误不会再次出现。感谢您简化Google文档!
Clocker


8

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

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


3

基本上,您可以尝试扩展规模来解决问题,Bitmap并且可以减少内存消耗。为此,您可以复制此处显示的方法。

另外,Android Developers上有专门的页面,可帮助您更好地了解如何加载大型位图。看一下官方文档


1

尽管以上答案显然是正确的,但一种更好的做法是在不再使用ImageView的bitmap / src属性时将其显式设置为null,主要是在您的活动被破坏时。其他任何繁重的资源(大文本,音频,视频)等也可以作废。这样可以确保立即释放资源,而不必等待GC收集。


社区,请您提供一些有关此方法的反馈!
基里尔·佐托夫

0

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

好,易于:

Glide.with(getContext())
            .load(imageFile.getPath())
            .into(previewImageView);

-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;
    }

-3

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


2
只是在不知道自己所做的事情的情况下更改inSampleSize或对其进行解释都不应成为答案。
JacksOnF1re
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.