安卓位图加载内存溢出错误Galaxy S3 WXGA

24

如果有人能够确认我是否正确地解决了以下问题,或者是否存在其他解决方案,我将非常感激。

我的应用程序会将一张大图(例如800*1720像素)加载到内存中,并在滚动视图中显示它。这张图片是一个博物馆的平面图,我想要一个简单的地图体验,可以进行滚动和缩放。在旧设备上,该图片可以成功加载,但在三星Galaxy S3上会导致内存不足错误。

通过查看LogCat消息,发现在创建位图时,分配了22MB的内存用于位图,而不是800*1720*4 = 5.5MB所需的内存。实际上,比其他设备多分配了4倍的内存,将内存使用量推超过50MB堆大小。

这个问题的推荐解决方案是使用BitmapFactory.Options.inSampleSize选项来减少加载图片时的分辨率,从而使其需要更少的内存。然而,这会降低图像质量,而我实际上想以原始质量的全尺寸显示它,因为在旧设备上效果很好。

经过长时间的思考,我得出结论,问题在于S3的WXGA屏幕的像素密度为2.0,因此对于图像中的每个像素,位图实际上会分配4个像素。通过一些试错,我发现可以通过设置options.inScaled = false;来防止这种情况发生。

http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inScaled

同时,我也意识到,通过使用较低的颜色深度(每个像素只用2个而不是4个)来设置options.inPreferredConfig = Bitmap.Config.RGB_565;,我可以将内存使用减少一半到2.7MB。对于我的平面图来说,这并未影响可见的图片质量。

最终代码如下:

String uri = "drawable/floorplan";
int imageResource = getResources().getIdentifier(uri, null, getPackageName());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inScaled = false;
Bitmap bm = BitmapFactory.decodeResource(getResources(), imageResource, options);
IVfloorplan.setImageBitmap(bm);

当显示这个位图时,您需要将其缩放回去。要计算缩放比例,您可以从以下内容中获取像素密度:

float density = getResources().getDisplayMetrics().density;

我将位图的内存使用量从22MB减少到2.7MB,在50MB堆中这是相当显著的。


3
好的分析,感谢您提供的解决方案。 - Simon
我认为在这里提供文档链接可能是个好主意,该链接部分描述了与inSampleSize相关的内容:http://developer.android.com/training/displaying-bitmaps/load-bitmap.html。还有一个关于玩转位图颜色格式及其优缺点的网站:http://www.curious-creature.org/2010/12/08/bitmap-quality-banding-and-dithering/。 - sandrstar
太棒了。我认为这对于其他堆大小较小的设备也会有帮助。但是你应该添加一个答案,而不是在问题本身中回答它,因为这违反了SO规则,并使问题免受删除的影响。 - Andro Selva
如果图像比设备屏幕大,您应该考虑缩小它,因为许多像素将不再使用。 - android developer
如果您能将解决方案发布为答案并自我接受,那就太好了。谢谢。 - Kev
1个回答

2
S3手机屏幕分辨率很高,所以很容易理解。此外,如果图像位于drawable文件夹中,则可能会自动放大。也许有方法可以关闭它。即使您的图像大小没有改变,操作系统也必须分配缓冲区来容纳图像,还可能需要一个缓冲区来显示图像在屏幕上。
另一种选择是使用平铺,这在类似SNES游戏的游戏中使用。这也是它们处理大量图形而不会耗尽RAM的方式。有证据表明这就是谷歌地图具有地球地图的方式。基本上, 您将大图像切成小块并在屏幕上显示这些块,当你移动时(当然,每边可能有1个额外的瓷砖)。
尽管这是在Honeycomb之后,Google放入了更好地管理Bitmap分配的代码,但是请小心Bitmap分配。它更像是C程序而不是Java程序,因此最好像管理C程序一样管理它。在Android中使用Bitmap对象时很容易耗尽堆空间。

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