Android Bitmap加载内存不足错误Galaxy S3 WXGA

时间:2022-08-26 21:35:16

I would be very grateful if someone could confirm that I have solved the below problem correctly or if there is an alternative solution?

如果有人能够确认我已正确解决了以下问题或者是否有替代解决方案,我将非常感激?

I have an app that loads a large image (e.g. 800*1720 pixels) into memory and displays it in a scrollview. The image is a floor plans for a museum and I wanted a simple map like experience of scrolling and zooming. The image loaded fine on older devices, but caused an out of memory error on a Samsung Galaxy S3.

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

Looking at the LogCat messages it turned out that in creaing the bitmap 22MB was being allocated for the bitmap instead of 800*1720*4 = 5.5MB. Essentially 4x as much memory was being allocated as required by other devices and pushing the memory usage over the 50MB heap size.

看看LogCat消息,结果发现在创建位图时,22MB正在为位图而不是800 * 1720 * 4 = 5.5MB进行分配。基本上按其他设备的要求分配了4倍的内存,并将内存使用量推到50MB堆大小。

The recommended solution to this problem is to use the BitmapFactory.Options.inSampleSize option to reduce the resolution of the image loaded and have it require less memory. However, this reduces the quality of the image, and I actually want to display it at it's full size in the original quality as works fine on older devices.

此问题的推荐解决方案是使用BitmapFactory.Options.inSampleSize选项来降低加载的图像的分辨率并使其需要更少的内存。然而,这降低了图像的质量,我实际上希望以原始质量以完整尺寸显示它,因为在旧设备上工作正常。

After much head scratching I concluded that the issue was that the pixel density on the S3's WXGA screens is 2.0 and thus for each pixel in the image, the bitmap was actually allocating 4 pixels. With a bit of trial and error I discovered I could prevent this happening by setting options.inScaled = false;

经过多次刮擦后,我得出结论,问题是S3的WXGA屏幕上的像素密度是2.0,因此对于图像中的每个像素,位图实际上分配了4个像素。通过一些试验和错误,我发现我可以通过设置options.inScaled = false来防止这种情况发生;

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

On the way, I also realised that I could cut my memory usage in half to 2.7MB by using a lower fidelity colour depth of 2 pixels instead of 4 pixels by setting options.inPreferredConfig = Bitmap.Config.RGB_565;. For my floorpans this didn't effect the visible image quality.

在途中,我还意识到通过设置options.inPreferredConfig = Bitmap.Config.RGB_565;我可以通过使用2像素而不是4像素的较低保真度颜色深度将我的内存使用量减半到2.7MB。对于我的地板,这不会影响可见的图像质量。

The final code was thus:

因此最终的代码如下:

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

When displaying this bitmap you need to scale it back up. To work out the scaling you can obtain the pixel density from:

显示此位图时,需要将其缩放。要计算出缩放比例,您可以从以下位置获取像素密度:

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

I reduced memory usage for the bitmap from 22MB to 2.7MB, which in a 50MB heap is significant.

我将位图的内存使用量从22MB减少到2.7MB,这在50MB堆中非常重要。

1 个解决方案

#1


2  

The screen of the S3 has a really high res, so it's quite understandable. Also, if the image is in the drawable folder, it could be getting upscaled automatically. There might be ways to turn that off. Even if your image size doesn't chance, the OS has to allocate a buffer to accommodate the image, also possibly one to accommodate showing it on the screen.

S3的屏幕有很高的分辨率,所以它是可以理解的。此外,如果图像位于可绘制文件夹中,则可能会自动升级。可能有办法将其关闭。即使您的图像大小不可能,操作系统也必须分配一个缓冲区来容纳图像,也可能需要一个缓冲区以便在屏幕上显示它。

An alternative is using tiling, which is used in games like SNES games. This is also how they handled lots of graphics without running out of RAM. Evidence shows that this is how Google Maps has a map of Planet Earth. You basically chop up the large image into tiles and show the tiles on the screen as you are panned to them (of course, maybe 1 extra tile on each side).

另一种方法是使用平铺,用于SNES游戏等游戏。这也是他们如何处理大量图形而不会耗尽RAM。有证据表明,这就是Google Maps拥有Planet Earth地图的方式。您基本上将大图像切割成瓷砖并在平移到屏幕上时显示瓷砖(当然,每侧可能有1个额外的瓷砖)。

Even though this is post-Honeycomb, where Google put in code to better manage Bitmap allocations, be careful with Bitmap allocation. It's more like a C program than a Java program, and it's a good idea to manage it like one. It's very easy to run out of heap space when using Bitmap objects in Android

即使这是后蜂窝,谷歌在其中放入代码以更好地管理位图分配,但要小心使用位图分配。它更像是一个C程序而不是Java程序,并且像一个程序一样管理它是个好主意。在Android中使用Bitmap对象时,很容易耗尽堆空间

#1


2  

The screen of the S3 has a really high res, so it's quite understandable. Also, if the image is in the drawable folder, it could be getting upscaled automatically. There might be ways to turn that off. Even if your image size doesn't chance, the OS has to allocate a buffer to accommodate the image, also possibly one to accommodate showing it on the screen.

S3的屏幕有很高的分辨率,所以它是可以理解的。此外,如果图像位于可绘制文件夹中,则可能会自动升级。可能有办法将其关闭。即使您的图像大小不可能,操作系统也必须分配一个缓冲区来容纳图像,也可能需要一个缓冲区以便在屏幕上显示它。

An alternative is using tiling, which is used in games like SNES games. This is also how they handled lots of graphics without running out of RAM. Evidence shows that this is how Google Maps has a map of Planet Earth. You basically chop up the large image into tiles and show the tiles on the screen as you are panned to them (of course, maybe 1 extra tile on each side).

另一种方法是使用平铺,用于SNES游戏等游戏。这也是他们如何处理大量图形而不会耗尽RAM。有证据表明,这就是Google Maps拥有Planet Earth地图的方式。您基本上将大图像切割成瓷砖并在平移到屏幕上时显示瓷砖(当然,每侧可能有1个额外的瓷砖)。

Even though this is post-Honeycomb, where Google put in code to better manage Bitmap allocations, be careful with Bitmap allocation. It's more like a C program than a Java program, and it's a good idea to manage it like one. It's very easy to run out of heap space when using Bitmap objects in Android

即使这是后蜂窝,谷歌在其中放入代码以更好地管理位图分配,但要小心使用位图分配。它更像是一个C程序而不是Java程序,并且像一个程序一样管理它是个好主意。在Android中使用Bitmap对象时,很容易耗尽堆空间