安卓上的位图大小超出了VM预算错误

3

我正在从资源文件夹中加载100张图片到一个数组对象中。这些图片非常小(每个png约20k),我使用以下代码来完成此操作,以防止内存泄漏和优化性能:

在循环中:

// create resized bitmap from asset resource
        InputStream istr = assetManager.open(pics[i]);
        Bitmap b = BitmapFactory.decodeStream(istr);
        b = Bitmap.createScaledBitmap(b, 240, 240, true);

其中pics[i]是一个包含在我的Asset文件夹中的文件名列表。

这段代码对我来说是有效的,但我仍然会不时地收到用户的错误报告(我可以在开发者控制台错误中看到):

java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:468)
at android.graphics.Bitmap.createBitmap(Bitmap.java:435)
at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:340)

有什么办法可以改善它吗?还是说这是安卓的世界,我们永远无法交付完美的应用程序?


非常正确,这个问题非常棘手,我希望这个链接会有所帮助:https://dev59.com/puo6XIcBkEYKwwoYTzNK - Mohammed Azharuddin Shaikh
3个回答

2

位图不会被压缩,因此它们存储在宽度 * 高度 * 4个字节中。这意味着每个图像使用约225 KB的内存。100张图片需要约22 MB的内存。最小堆大小为16 MB,但设备通常具有24+ MB堆。而这个堆不仅用于数据,还用于活动、视图等等。这意味着您不能加载100个这样大小的位图。


谢谢。我有一种感觉,我在这里做错了什么 :) - Ofershap

2
当然,您可以改进一些东西:不要同时加载太多图片到内存中(或者减小它们的大小)。一些设备可用内存很少。您的设备可能具有比某些客户手机更高的堆限制。因此,对于他们来说它崩溃了,而对于您来说它可以工作(请参见此视频以了解一些堆限制[在4:44处])。
您可以通过ActivityManager.getMemoryClass()获取可用堆大小。
为了改进这个问题,测试您现在需要哪些图片(即正在显示的图片)和哪些不需要。只加载所需的图片,并回收您不再需要的位图。
另外,请尝试使用BitmapFactory.decodeResource()BitmapFactory.Options.inSampleSize。这允许您直接以较低分辨率加载图像,而无需将它们加载到完整大小,然后像您在此处所做的那样调整其大小。

BitmapFactory.decodeResource() 是用于从 Res 加载图像的,对吧?但我是从 Asset 中加载它们。 - Ofershap
谢谢您的评论,我已经重写了代码。当图片被显示时,我才会加载它们。这个 Android 中的内存泄漏问题让我写了糟糕的代码。 - Ofershap
嘿,decodeResource() 是用于可绘制资源的。抱歉,我没有完全意识到你在使用资产。但是还有一个 decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) 函数,不确定它是否对你有用,我自己从未使用过它。 - user658042
是的,我的问题已经解决了!我使用了hotveryspicy提供的链接建议,该建议建议使用options.inSampleSize,从那时起就没有内存泄漏错误了。 - Ofershap

1

通常情况下,您只需要展示用户实际可见的内容。将其他任何东西(如图形)保存在内存中只是锦上添花。这就是为什么具有更高分辨率屏幕的设备将拥有更大的堆大小可用的原因。

例如,在320x480屏幕上,您最少只需要320x480x4=614400(600kb)。

考虑到这个概念,您需要考虑是否需要在内存中保存100个位图。用户是否同时查看100个位图?在这种情况下,您可以降低图像质量而不会降低用户体验(屏幕上只有那么多像素)。用户是否正在滚动浏览100个位图?然后根据需要动态卸载和加载图像(带有一些缓存以保持平稳)。

总是有解决方法。


即使我只在用户查看图像时加载它们,它仍然抛出了异常。在Android中加载图像存在一些内存泄漏问题。 - Ofershap
这就是为什么我写了这段代码(基于我在某个地方看到的一些示例):InputStream istr = assetManager.open(pics[i]); Bitmap b = BitmapFactory.decodeStream(istr); b = Bitmap.createScaledBitmap(b, 240, 240, true); - Ofershap

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