Android Words of Wisdom? Catch Everything

Every mobile platform has its pitfalls that separate the great apps from the “oh my god, they released THAT!” apps.

Most of these landmines revolve around a couple of vital issues:

1) Gracefully failing when something “bad” happens (network connection drops, memory is in scarce supply, battery is dying, something is null that’s not supposed to be null, etc.), and

2) Knowing that you NEED to gracefully fail before the Operating System heaps a big helping of fail on you, pre-emptively, for kicks and giggles.

By gracefully failing, I mean doing something to mitigate any negative effects to a user (closing open files if the app needs to shut down, alerting the user to a problem without simply crapping out) in an orderly way, and NOT simply having your app exit.

And by “knowing” that you need to take some action on behalf of your application, I simply mean that you need to put in as much error handling – everywhere – in your applications as you can possibly stomach.

In the case of Android applications, this means that wherever possible, you need to be using try / catch in code that has any chance of failing on IO errors, communications, or low memory.

For most libraries that throw exceptions, developer environments like Eclipse will tell you that you need to wrap certain calls in try / catch or the app won’t compile.  BUT – and this is a BIG BUT – try / catch won’t catch everything, unless you tell it what to catch.

Where this usually winds up biting you in the ass, especially with Android and its 16MB VM limit, is reading large photos from the camera’s gallery. While Eclipse will “let” you compile code to read bitmaps from disk with just a catch for IOExceptions, it will not require that you put in a catch for OutOfMemoryError – which is by far the error you’ll encounter the most when working with large numbers of bitmaps within an Android app.

So… what’s a careful developer to do?

At a minimum, this:

1) Catch every possible error / exception within as many blocks of code as you can.  I don’t always live by this dictum, much to my eventual chagrin – but I do try (no pun intended).

2) Never read a bitmap from your Android device without “downsampling” it first. You’ll almost always run out of memory, especially with the newer 8 megapixel devices.  The following method will read a bitmap from a device and sample it down about as safely as one can on Android:


public Bitmap readBitmap(Uri selectedImage) {
Bitmap bm = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;

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 (OutOfMemoryError e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
return bm;
}

This is about as much as we can do to mitigate “bad things” happening when reading bitmaps into our apps, but we can also do one more thing to help, and that is

3) Release any bitmap memory as soon as we are through using it. I usually use a method like the following:



// Clear bitmap
public static void clearBitmap(Bitmap bm) {
bm.recycle();

// revised 08/15/2010. As a commenter astutely pointed out, bm is not a passed reference.
// To free the memory associated with this bitmap (as intended),
// bm should be set to null in the caller.
// bm = null;
System.gc();
}

Recycle frees up the memory associated with this 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.

Assigning null to the bitmap will free its memory.

And calling System.gc() indicates to the virtual machine containing your app that it would be a good time to run the garbage collector.

Solid error handling and graceful program exits are often times more art than science. But like anything else in this life, one can never have too much of a good thing.

Try {&&} catch (everything) {YouCan();}

About these ads

7 thoughts on “Android Words of Wisdom? Catch Everything

  1. Very helpful post. Hope to read more soon.

    Like

  2. [...] help you resolve this problem. It's also good practice for ALL of your code. This link might help: http://davidjhinson.wordpress.com/20…ch-everything/ ALSO: you might want to use "logcat" while running your program (I assume you're running [...]

    Like

  3. Les says:

    David, thanks for this thoughtful blog. BTW, I LOVE your writing style, i.e., frank, poignant and humorous. Have a great day!

    Like

  4. Glenn says:

    Hi David,

    Thanks for this, this is the closest I’ve found to dealing with this situation, but with your readBitmap method, won’t you be generating a NullPointerException if your FileNotFoundException is raised?

    ‘fileDescriptor’ will be null, yet you reference it in the ‘finally’, which doesn’t deal with the null pointer exception raised.

    This is what I’m experiencing at least.

    Thanks again.

    Like

    • davidjhinson says:

      If file not found is raised, my expectation is that bm is null, and that I would test that in my caller. But I will play around with this this weekend and see if I missed something – which is probably the case.

      Like

  5. Tim says:

    You shouldn’t call System.gc() in this case (or really any case). Should dalvik decide it needs more memory, it will collect on its own. This method causes a full garbage collection, over all generations, which is an immense overhead.

    It’s especially useless in this case. The reason the bitmap class has a release method in the first case is because bitmap data isn’t held in the Java heap. So garbage collection will do nothing for you here. It’s held in the native heap, which still counts against the memory limit.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 7,999 other followers