Pages

Wednesday, August 4, 2010

Give me more memory, please (avoiding OutOfMemory errors)

Source code of the examples referenced in this post is available at http://code.google.com/p/intexsoft/

When somebody writes an android application which use graphics extensively he may get OutOfMemory errors very fast. This is because android grants the relatively small amount of memory to the application process. When we, at Intexsoft, started to develop “My seven wonders” application we also got a number of problems related to memory usage and in this post we want to share some thoughts on how to avoid them.

The application we developed has different compass schemes, which possess a number of graphical elements, such as background, compass circle and compass needle.



Originally to make schemes switching faster we were preloading images for all the schemes during startup. Then we were able to call any of the scheme getter methods to retrieve images without care about are they available or not. But after we’ve created several schemes we got OutOfMemory error.

One of the ways to work around this is to load the graphics data on-demand. Means, we should load images only in case we need them and release the memory when we do not.
First we replaced the scheme initialization from objects based (when we passed the Bitmaps to it) to the id based (when we pass the resource identifiers).

Also we created a loadResources() method, which was initializing bitmap objects, based on their identifiers. To free the used resources we introduced a freeMemory() method, which just sets appropriate bitmaps pointers to null so these object will be recycled on a next run of garbage collector. Then in CompassSurfaceView. switchScheme() method we are explicitly calling loadResources() for the new scheme and freeMemory() for the old scheme.

If we’ll run the example and look at the memory usage through DDMS we’ll see it almost does not change even when we switch over 15 different graphical schemes.



In the example above we definitely needed bitmap objects to operate with them. But there are many cases, when we do not need to load bitmap objects at all. It is a good rule not to load Bitmap objects if you are able to do the required things by just passing their identifiers to the API.
For example we needed to overlay the check mark on the selected thumbnail item in the GridView.



We added a number of ImageViews to the GridView. Then for the selected bitmap we were loading two images – the thumbnail and the image with a checkbox. Then we were overlaying one above the other using Bitmap graphics. The resulting generated image was passed through ImageView.setImageBitmap method. The plus was that this method worked, but it was taking too much memory and was a little bit slow.

Then we changed the logic of handling of selected thumbnail. Instead of a single ImageView we used the FrameLayout, where we placed ImageView with the thumbnail and an ImageView with a marker. For both ImageViews the image data was passed as not an object but as resource identifier through ImageView.setImageResource method. As a result we got rid of memory problem and received a performance gain.

To summarize:
* Try to find a way or a workaround to operate with Bitmap resource identifiers instead of loading of Bitmap objects.
* When you definitely need to load a bitmap, do this, but remove any pointers to it when this bitmap is no longer required.

No comments:

Post a Comment