Scarce Commodities – Google Android, Memory, and Bitmaps

Scarce Commodities – Google Android, Memory, and Bitmaps

Working on mobile devices forces one to make conscious decisions regarding coding choices, if for no other reason that resources are scarce (memory, screen size, bandwidth). Taking the easy route and ignoring wise mobile programming practices can take what could be a promising application and make it a disappointing user experience.

If you’ve spent any time with the Google Android SDK, and have tried to read a JPEG into a Bitmap using Media.getBitmap, you’ve almost certainly run into this little gem of an error message:

bitmap size exceeds VM budget

Unfortunately, since Android caps all applications’ VMs at 16MB in size, it only takes one or two big image reads to get you into trouble, regardless of all the garbage collection and Bitmap recycles you may try (see code snippet at the end of this post for more on that).

So, what’s a programmer to do?

Well, the only thing one can do is to read only what you need into memory.  That means that you won’t be able to read in that 10MB jpeg sitting on your phone (at least, you won’t be able to read it reliably and / or repeatably) without trimming it down a bit.

The code below will do this for you; rather than calling Media.getBitmap, use this readBitmap function instead:

 

 

// Read bitmap
public Bitmap readBitmap(Uri selectedImage) {
Bitmap bm = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 5;
AssetFileDescriptor fileDescriptor =null;
try {
fileDescriptor = this.getContentResolver().openAssetFileDescriptor(selectedImage,”r”);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
finally{
try {
bm = BitmapFactory.decodeFileDescriptor(fileDescriptor.getFileDescriptor(), null, options);
fileDescriptor.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return bm;
}

 

The magic fairy dust in this function that allows you to trim down large bitmaps into digestible sizes is the options.inSampleSize property.  inSampleSize – in this instance – takes a bitmap and reduces its height and width to 20% (1/5) of its original size.  The larger the value of inSampleSize N (where N=5 in our example), the more the bitmap is reduced in size.

There’s also another coding practice that one should always use when dealing with Bitmaps in Android, and that is the use of the Bitmap recycle() method. The recycle() method frees up the memory associated with a bitmap’s pixels, and marks the bitmap as “dead”, meaning it will throw an exception if getPixels() or setPixels() is called, and will draw nothing.

In my projects, I have a helper function that does cleanup when I am finished using a Bitmap, named clearBitmap:

// Clear bitmap

public static void clearBitmap(Bitmap bm) {

bm.recycle();

System.gc();

}

 

Dealing with memory errors on Android apps seems to be the “problem child” issue that crops up the most (like too many threads on Blackberry apps – different post for a different day).

But like parenting a problem child, what’s called for is attention and intention to mitigate havoc wreaked.

14 thoughts on “Scarce Commodities – Google Android, Memory, and Bitmaps

  1. Thanks….This code is working….But what about the image quality ?
    That question remains always unanswered….Please if possible, give me solution over that…

    Like

    1. If you want higher resolution images, drop the inSample number. 5 = 1/5 original size, 4=1/4 original image size, … 1=no resampling (original size).

      I only develop on Android, and I’m certainly not an Android apologist – though I seem to find myself explaining it’s shortcomings enough to be one.

      It is what it is – the trade off is always risking reading in a large enough image to blow up the heap, or sampling down the size to a safe enough size to keep your app from Force Closing.

      Like

  2. What does the instruction
    bm = null
    accomplishes?
    It is a local variable and The method caller still has the original reference to the object. So it doesn’t help.
    The real deal is the call to recycle.

    Like

  3. Hi,

    I am using drawable in XML file(like my imageswitcher) instead of using bitmap and I dont know how to implement it..Do you have the solution to the drawables? thanks!! (:

    Like

  4. You blindly resample by 5 regardless of the source size and the destination requirements. It would be more useful to see what the original size is before just arbitrarily sampling by 5.

    In your finally you reference a possible null value for fileDescriptor.

    In your cleanup routine the bm = null is totally useless.

    Like

  5. So this really doesn’t solve the problem, it will only delay it.

    options.inSampleSize = 5

    this will reduce the size and therefor take less memory, but wouldn’t it run out of memory eventually if you keep calling the function? Let’s say the app uploads photos to the server. Use this function to read the image, lower the quality, resize and send it away. So it will work for a while, but wouldn’t it run out after uploading a few?

    Like

  6. Isn’t it possible to check the available heap size before trying to pull in a new image? If there is no enough free memory, simply skip the actual image.

    Like

  7. Reblogged this on 300 Words, 2 Minutes and commented:

    This week has thrown a severe monkey wrench into my daily schedule. Sometimes, life just happens.

    But to make it up to you, O Gentle Reader, I share with you one of my most traffic posts from Logorrhea.

    Enjoy.

    Like

Leave a comment