Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Bitmap loading out of memory error Galaxy S3 WXGA

Tags:

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.

like image 659
Huntzz Developers Avatar asked Sep 29 '12 10:09

Huntzz Developers


1 Answers

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.

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

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

like image 148
Joe Plante Avatar answered Sep 28 '22 03:09

Joe Plante