My Android app has been live in Google Play for 6 months, but I'm still encountering strange bugs and behaviours, making big mistakes, and learning surprising things. The latest surprise has come with a recent flush of ICS users who's devices are putting more significant demands on my handling of the Activity lifecycle, specifically with relation to managing state.

tl:dr; - beware when invoking startActivityForResult that onActivityResult is invoked before onResume!

Before I get to the problems, lets have a quick look at the Activity lifecycle. I'm going to "borrow" google's lifecycle diagram:

Android Activity Lifecycle

Some important things to remember here are:

  • Apps typically consist of multiple Activity's, and each Activity follows the above lifecycle while your app is running.
  • When your Activity starts a child Activity (with startActivity or startActivityForResult), both the onPause and onStop lifecycle methods of the parent Activity should be called, in that order.
  • When an Activity is invoked as a child Activity, its lifecycle will be completed by the time the parent Activity is fully in control again (at least onCreate, onStart, onResume, and onPause will have been invoked).
  • Your Activity can be killed off at any time after its onPause has completed, without necessarily passing through onStop or onDestroy. It is critically important to remember that this includes situations where your Activity is on the back-stack waiting for a result from a child Activity, or even when it is still visible but mostly covered by a dialog!

With regard to the last point its worth familiarising yourself with the way Android manages Processes.

State, and the Application object

One simple way, you might think, to manage state without worrying too much about the Activity lifecycle is to use the Application object. Android allows you to specify your own class (extends Application), which your Activity's can access through getApplication.

That's nice. It needs care though, since the process that your Application object lives in can be killed and restarted at (perhaps) unexpected junctures. Take this scenario:

  1. App A starts with Activity A1, which sets up some state in the Application object.
  2. Activity A1 starts Activity A2, which uses the state in the Application object.
  3. Activity A2 fires an Intent for Activity B1 of App B and expects some result (lets say we fired an Intent asking for an image to be captured by the camera app).
  4. App B starts, and launches Activity B1.
  5. Activity B1 is memory-heavy, so the system shuts down App A (completely kills its process), even though it is on the back-stack waiting for a result.
  6. Activity B1 returns, app A's Application object is created, Activity A2 is started again but Activity A1 never launched in the lifetime of this Application object so does not get the opportunity to set up the state of the Application object.

The sequence diagram might look something like this:

seuqence diagram

Clearly, if Activity A2 relies on A1 having run first to set up the application state, there's going to be trouble as soon as A2 starts trying to access that state after resuming from B2. If you're going to use the Application object to manage state, make sure that it is set up as part of the Application's own lifecycle methods.

Now, the gotcha that's been hurting me is this: I assumed that onActivityResult would be invoked after onResume. Turns out this is not the case, and in fact onActivityResult was getting called long before my state was re-initialised in onResume.

On my devices I never suffered from this because my process was not being killed and the state was still present in memory at the point when onActivityResult was invoked!

blog comments powered by Disqus