mark cerqueira well-rounded nerd

Surviving Death - Android Activities

It’s a fact of life: your Android activities aren’t always going to be the center of attention. They can be backgrounded either because your entire app is backgrounded or because another activity of your app is pushed onto the activity stack. Even sadder, while backgrounded, your activity (and its state) can be entirely destroyed. If you don’t handle this case, the eventual return of your activity will be less-than-ideal. Fortunately, it’s not that hard to handle this problem.

1. Use Android Annotations. Besides being used in the forthcoming examples, Android Annotations helps you write simpler code. Everyone loves simpler code. Get it now!

2. Develop and test with “Don’t keep activities” option turned on. Under normal circumstances, activities can be destroyed when you background them in low-memory situations. Enabling this option (in Settings ~> Developer Options) will force a pathological low-memory environment and activities will be destroyed as soon as you leave them. Don’t be lazy. Turn on this option, and make sure your activities can handle getting sent to the grave and getting revived.

3. Use static methods to generate intents for your activities. Using helper methods to generate ready-to-go intents forces you to pass data you’ll need for your activity to function properly. The data you pass in to these methods is data you will likely be concerned about when activities start getting destroyed and recreated. This is also useful if you need to later pass additional data to your activity. Add any new parameters, hit compile, and fix all the errors!

// this static method generates an intent for starting the ResilientActivity class
public static Intent generateResilientActivityIntent(Context context, String accountId) {
	// _ after the class name because it's using Android Annotations
    Intent intent = new Intent(context, ResilientActivity_.class);
    intent.putExtra(ACCOUNT_ID_KEY, accountId);
    return intent;
}

4. Figure out what to save when an activity is destroyed and @InstanceState it. Both of these are easy. First, any of the parameters you pass to your intent-generating methods you likely want to save. You’d used them to create the activity, so you’ll want them again when you recreate the activity. For these values, create fields and annotate them with @InstanceState. If your activity is destroyed, the code generated by Android Annotations will automagically save these fields to the bundle. If you’ve got complex objects, this will work if they implement the Parcelable interface.

// fields annotated with @InstanceState cannot be private
@InstanceState protected String mAccountId;

5. In onCreate, @InstanceState will be handled for you so just check for a null savedInstanceState and parse the data out yourself. If you’ve set everything else up, the only thing left to do is figure out what state you’re in in your onCreate method. If the activity is getting created for the first time, the Bundle passed in will be null and you’ll want to parse out all that data you stashed into your Intent. Otherwise, you’re getting recreated and the onCreate super method will handle restoring all annotated fields (courtesy of Android Annotations).

@Override
public void onCreate(Bundle savedInstanceState) {
    // if we are getting recreated, savedInstanceState will not be null

    // the call to super.onCreate will handle restoring any fields annotated with @InstanceState
	super.onCreate(savedInstanceState);

    // if savedInstanceState is null, we are getting created for the first time,
	// so parse out the data from the intent
	if(savedInstanceState == null) {
		mAccountId = getIntent().getStringExtra(ACCOUNT_ID_KEY);	
	}
}

That’s it! You can test the resilience of your activities by backgrounding your application or going to another activity and then returning back to that activity. In the hands of your customers, expect your app to be backgrounded for long periods of time and running on devices where a plethora of other apps are running. Eventually, an activity is going to pay the price and get sent to the grave to free up resources. Will your app’s return to life be elegant and seamless, or a disaster?