24

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.

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.

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.

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;

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.

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.

4

1 回答 1

2

S3的屏幕分辨率非常高,可以理解。此外,如果图像位于可绘制文件夹中,它可能会自动放大。可能有办法关闭它。即使您的图像大小没有机会,操作系统也必须分配一个缓冲区来容纳图像,也可能分配一个缓冲区来容纳在屏幕上显示它。

另一种方法是使用平铺,它用于 SNES 游戏等游戏。这也是他们在不耗尽 RAM 的情况下处理大量图形的方式。有证据表明,这就是谷歌地图拥有地球地图的方式。您基本上将大图像分割成图块,并在平移到它们时在屏幕上显示图块(当然,每边可能有 1 个额外的图块)。

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

于 2012-10-01T14:18:33.297 回答