Friday, February 5, 2010

Rebel Without A Context

The scenario is common enough. You want to implement some task in a background worker thread, like fetching data from an HTTP server or whatnot, and then at a future point the UI needs to be updated, based on whatever has or is happening in the worker thread.

So here's your naive Thread/Runnable implementation :

And there you have it. That's the most succinct implementation of the anti-pattern many people use. And certainly I must concede my own guilt of having used it.

The problem is that your background thread, in order to update the UI, is now tightly coupled to the Activity/Context that created it. So when/if an event occurs (such as an orientation change) that causes your Activity to be restarted, your background thread will continue to happily chug along and then try to update the UI even though it's still referencing the old Activity/Context. Your app is then likely to experience leaked window exceptions and other types of nastiness.

I've seen this problem attacked in different ways, generally trying to enable the background thread to update the UI on the new Context. The droidfu library has an AsyncTask implementation called BetterAsyncTask that does exactly that.

But this isn't a perfect solution in my opinion. Imagine that an important event occurs in the background thread, one that is serious enough that the user absolutely should be informed if they continue to use the app. What if it happens in the middle of an orientation change, after the old Activity has been destroyed, yet before the new Activity is fully initialized? A quick glance at BetterAsyncTask's source suggests that the message would never get delivered:

Now before I get into what I don't like about this, I just want to say that I applaud the creators of droidfu for releasing an open source library, and I've never used it myself, but that's my five second synopsis of it's implementation. It probably works fine for a lot of people, but I demand something more robust.

The way I see it, this just doesn't escape the anti-pattern. The core point that I'm trying to make here is this:

It's your Activity's responsibility to update the UI, not the responsibility of the worker thread.

Let me say that again, your background thread should not try to update the UI. What it can and should do is track it's own internal state, and allow itself to be queried by the active Activity instance. The simplest way to accomplish this is to use onRetainNonInstanceConfiguration to pass your Thread/AsyncTask implementation to the newly created Activity. The Activity can then query the state of your thread and deal with any impending UI events that it needs to process.

A simple, but effective way to accomplish this is with a polling Handler loop running in the UI thread. Something like this will work fine as long as you remember to shutdown the loop as your activity exits.

If this isn't realtime enough for you, then another approach would be to have your Activity register listeners with your thread, much like binding to a service and registering a remote callback. In fact, that's exactly how you should think about your background thread, as a lightweight service, that is decoupled from any direct UI interaction.

And just a quick warning as I wrap up, If you go the listener route, you still must be aware that events can occur when your Activity is being restarted, and before the new activity can register another listener instance.

I hope this article helps a few of you out there, or at the very least, shows you a different approach to a common problem.

Monday, February 1, 2010

Android Sweater

We didn't want to be left out of the Android arts and crafts mania, you know, the
various android pillows and dolls, so grandma hand knitted this sweater for Maxim.