Android Activity Lifecycle and Instance Management

The Problem

I recently had cause to take a very close look at how the Android OS and the Dalvik VM manages instances of Activity objects when starting and stopping your Activities.  I noticed on newer phones that class member values, and even static member values have the potential getting reset to their initialization state.  Whether or not that happens depends on how the cell phone manufacturer implemented the Android OS.  You will also notice when you run your application that the Activity lifecycle callbacks (e.g. onCreate, onResume, etc.) will be called  multiple times when an activity is displayed.

The only logical answer to this is some Android phones create instances of your Activity classes more than once when the Activity is put onto the UI stack.  And by created more than once, I mean it appears that either A) the entire VM managing the Activity is restarted, or B) the class loader is completely de-allocating your class definitions.

What I do know is this: the only thing you are guaranteed in Android activities is that Activity.onSaveInstanceState(Bundle) is called when Android needs you to save the member variables of your class. This recommendation applies to static class members as well.  Anyone not heeding this rule risks having things run just fine on one phone, and failing on another.

A Demonstration

For example, take this handy Activity class that sets up a couple constants useful for debugging:

package com.wordpress.rschilling;

public final class DebugConstants {
	public static final boolean DEBUG_LIFECYCLE = true;
	public static final String LOG_LIFECYCLE = "MyApp.LIFECYCLE";

	private DebugConstants() {
	}
}

And also this Activity class:

package com.wordpress.rschilling;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class LoggingActivity extends Activity {
	private boolean newinstance = true;
	private static boolean newstatic = true;

	public LoggingActivity() {
		super();
		if (DebugConstants.DEBUG_LIFECYCLE)
			Log.d(DebugConstants.LOG_LIFECYCLE, ": "
					+ this.getClass().getName() +
					" newstatic = " + newstatic);
		newstatic = false;
	}

	@Override
	protected void onPause() {
		super.onPause();
		if (DebugConstants.DEBUG_LIFECYCLE)
			Log.d(DebugConstants.LOG_LIFECYCLE, ": onPause "
					+ this.getClass().getName() +
					" newinstance = "
					+ newinstance);
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if (DebugConstants.DEBUG_LIFECYCLE) {
			String nullBundle = savedInstanceState == null ? "null"
					: "not null";
			boolean reinitialized = savedInstanceState != null ? true : false;
			Log.d(DebugConstants.LOG_LIFECYCLE, ": onCreate(bundle = "
					+ nullBundle + ", reinitialized = " + reinitialized + ") "
					+ this.getClass().getName() + " newinstance = "
					+ newinstance);
		}
		newinstance = false;
	}

	@Override
	protected void onStart() {
		super.onStart();
		if (DebugConstants.DEBUG_LIFECYCLE)
			Log.d(DebugConstants.LOG_LIFECYCLE, ": onStart "
					+ this.getClass().getName() + " newinstance = "
					+ newinstance);
	}

	@Override
	protected void onRestart() {
		super.onRestart();
		if (DebugConstants.DEBUG_LIFECYCLE)
			Log.d(DebugConstants.LOG_LIFECYCLE, ": onRestart "
					+ this.getClass().getName() + " newinstance = "
					+ newinstance);
	}

	@Override
	protected void onResume() {
		super.onResume();
		if (DebugConstants.DEBUG_LIFECYCLE)
			Log.d(DebugConstants.LOG_LIFECYCLE, ": onResume "
					+ this.getClass().getName() + " newinstance = "
					+ newinstance);
	}

	@Override
	protected void onStop() {
		super.onStop();
		if (DebugConstants.DEBUG_LIFECYCLE)
			Log.d(DebugConstants.LOG_LIFECYCLE, ": onStop "
					+ this.getClass().getName() + " newinstance = "
					+ newinstance);
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		if (DebugConstants.DEBUG_LIFECYCLE)
			Log.d(DebugConstants.LOG_LIFECYCLE, ": onDestroy "
					+ this.getClass().getName() + " newinstance = "
					+ newinstance);
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		if (DebugConstants.DEBUG_LIFECYCLE)
			Log.d(DebugConstants.LOG_LIFECYCLE, ": onSaveInstanceState "
					+ this.getClass().getName() + " newinstance = "
					+ newinstance);
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		super.onRestoreInstanceState(savedInstanceState);
		if (DebugConstants.DEBUG_LIFECYCLE)
			Log.d(DebugConstants.LOG_LIFECYCLE, ": onLoadInstanceState "
					+ this.getClass().getName() + " newinstance = "
					+ newinstance);
	}
}

Run on the Emulator

Put those into a new Android Project in Eclipse and run the application in the debugger on the emulator. You’re may just see some output like this:

08-18 09:57:33.508: DEBUG/MyApp.LIFECYCLE(824): : <init>com.wordpress.rschilling.LoggingActivity newstatic = true
08-18 09:57:33.538: DEBUG/MyApp.LIFECYCLE(824): : onCreate(bundle = null, reinitialized = false) com.wordpress.rschilling.LoggingActivity newinstance = true
08-18 09:57:33.538: DEBUG/MyApp.LIFECYCLE(824): : onStart com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 09:57:33.538: DEBUG/MyApp.LIFECYCLE(824): : onResume com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 09:57:43.688: DEBUG/MyApp.LIFECYCLE(824): : onSaveInstanceState com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 09:57:43.738: DEBUG/MyApp.LIFECYCLE(824): : onPause com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 09:57:43.758: DEBUG/MyApp.LIFECYCLE(824): : onStop com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 09:57:43.758: DEBUG/MyApp.LIFECYCLE(824): : onDestroy com.wordpress.rschilling.LoggingActivity newinstance = false

<< ROTATE THE EMULATOR HERE – CTRL+F12, or KEYPAD_7, or FN+CTRL+F12 (on the MAC) >>

08-18 09:57:43.788: DEBUG/MyApp.LIFECYCLE(824): : <init>com.wordpress.rschilling.LoggingActivity newstatic = false
08-18 09:57:43.798: DEBUG/MyApp.LIFECYCLE(824): : onCreate(bundle = not null, reinitialized = true) com.wordpress.rschilling.LoggingActivity newinstance = true
08-18 09:57:43.818: DEBUG/MyApp.LIFECYCLE(824): : onStart com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 09:57:43.818: DEBUG/MyApp.LIFECYCLE(824): : onRestoreInstanceState com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 09:57:43.818: DEBUG/MyApp.LIFECYCLE(824): : onResume com.wordpress.rschilling.LoggingActivity newinstance = false

This output is from running just one instance of the application.  Notice the two lines that have “newstatic = true”, and remember that this is just one instance of the application running on a phone.  The static variables in the class were maintained by the emulator, but the class instance was wiped out and re-instantiated.  This means that the Dalvik virtual machine kept the class definition in memory, but the class instance got garbage collected.

Also notice that onLoadInstanceState(Bundle) was NOT called.  The bundle that was passed to onSaveInstanceState(Bundle) was send to onCreate().

Run on a Phone

If I run this on one of my Android  phones made by Samsung I get this:

08-18 10:00:49.633: DEBUG/MyApp.LIFECYCLE(4152): : <init>com.wordpress.rschilling.LoggingActivity newstatic = true
08-18 10:00:49.643: DEBUG/MyApp.LIFECYCLE(4152): : onCreate(bundle = null, reinitialized = false) com.wordpress.rschilling.LoggingActivity newinstance = true
08-18 10:00:49.643: DEBUG/MyApp.LIFECYCLE(4152): : onStart com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 10:00:49.643: DEBUG/MyApp.LIFECYCLE(4152): : onResume com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 10:00:53.173: DEBUG/MyApp.LIFECYCLE(4152): : onSaveInstanceState com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 10:00:53.173: DEBUG/MyApp.LIFECYCLE(4152): : onPause com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 10:00:53.173: DEBUG/MyApp.LIFECYCLE(4152): : onStop com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 10:00:53.173: DEBUG/MyApp.LIFECYCLE(4152): : onDestroy com.wordpress.rschilling.LoggingActivity newinstance = false

<< ROTATE THE EMULATOR HERE – CTRL+F12, or KEYPAD_7, or FN+CTRL+F12 (on the MAC) >>

08-18 10:00:53.203: DEBUG/MyApp.LIFECYCLE(4152): : <init>com.wordpress.rschilling.LoggingActivity newstatic = false
08-18 10:00:53.203: DEBUG/MyApp.LIFECYCLE(4152): : onCreate(bundle = not null, reinitialized = true) com.wordpress.rschilling.LoggingActivity newinstance = true
08-18 10:00:53.213: DEBUG/MyApp.LIFECYCLE(4152): : onStart com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 10:00:53.213: DEBUG/MyApp.LIFECYCLE(4152): : onRestoreInstanceState com.wordpress.rschilling.LoggingActivity newinstance = false
08-18 10:00:53.213: DEBUG/MyApp.LIFECYCLE(4152): : onResume com.wordpress.rschilling.LoggingActivity newinstance = false

Notice, once again the activity class is destroyed and then re-instantiated when the screen rotates. All non-static variable values are lost while static variables retain their value.  And, consistent with the documentation, the only thing that we are guaranteed is that onSaveInstanceState(Bundle) is called before the activity is destroyed (which happens just prior to the rotation).

Don’t Rely On Static Variables Anyway

Even though we can see that in this example, the static variable values were preserved, it’s important to not rely on them.  The Virtual Machine and the OS determine when class definitions are retained in the virtual machine.  If the operating system decides it needs more memory to display a new activity, it has the option of removing your entire Activity class from the virtual machine, and that means your static variables will be lost.

The documentation of the Activity.onSaveInstanceState(Bundle) class contains a clue:

This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state. For example, if activity B is launched in front of activity A, and at some point activity A is killed to reclaim resources, activity A will have a chance to save the current state of its user interface via this method so that when the user returns to activity A, the state of the user interface can be restored via onCreate(Bundle) or onRestoreInstanceState(Bundle).

 

The Conclusion

Make sure you follow the API’s expected behavior otherwise the values of your variables will be lost.  Here’s a short checklist to make sure you’ve got your bases covered:

  1. save the values of all your class variables, including static variables in onSaveInstanceState(Bundle) to the bundle.
  2. restore the variables from the bundle (if it’s not null) in onCreate(Bundle), and onRestoreInstanceState(Bundle).

Android AccountManager and SyncAdapter … WTF?

Ever try to write an application that uses Android’s AccountManager and SyncAdapter to store user credentials?  Wow, what a pain in the arse.

I’ll save you all the ranting and just get down to it.

First, a bit of consolation: writing the code to get your application working with AccountManager is probably one of the more overly complicated things you can do on Android, and one of the most poorly documented features on any cell phone ever.

Google really dropped the ball on this one.  So, I’ll do my best to fill in the gaps.  But be warned, my writing here will be incomplete until I get all my questions answered.  Cause, well, the information I can get is incomplete.

Examples

Google’s SyncAdapter example exists, but there’s no Google documentation that shows you WHY the example was written the way it was.  As with all examples, you must be able to change things to get them to work in your application, so you can’t use them verbatim.  And that’s where the problems come in.

Final concept has a nice example, but again it only solve one type of problem.  And it doesn’t go over the things to avoid when working directly with the AccountManager.

Show AccountManager Output in LogCat

The first thing you’re going to need to do is get logcat to dump AccountManager messages.  You can do this by entering this on your command line:

$ adb shell setprop log.tag.AccountManagerService VERBOSE
$ adb shell setprop log.tag.Accounts VERBOSE
$ adb shell setprop log.tag.Account VERBOSE
$ adb shell setprop log.tag.PackageManager VERBOSE

(source: http://stackoverflow.com/questions/3774282/securityexception-caller-uid-xxxx-is-different-than-the-authenticators-uid )

You can set the log level of any tag with this pattern,  See http://developer.android.com/reference/android/util/Log.html#isLoggable(java.lang.String,%20int)

Notes From The Android Source & Distribution

The best way to get your head around AccountManager, or any part of Android for that matter, is to look at the source, and the distribution packages.  This section covers what I’ve found.

<android-sdk>/platforms/android-X/framework.aidl

This file contains a list of Android Interface Definition Language (AIDL) interfaces that are used within the Android Framework (the operating system components above the hardware).  Within this we find a few key definitions:

interface android.accounts.IAccountManager;
interface android.accounts.IAccountManagerResponse;
interface android.accounts.IAccountAuthenticator;
interface android.accounts.IAccountAuthenticatorResponse;

This is a big clue that IDL bindings are used when talking to the account manager. We should be able to look at the code that implements these interfaces and see what they do.

Attach Android Source to Your Project

This is actually fairly straightforward once you know what you’re looking for.  You simply create a directory, initialize it with the repo command and synch up with the source.  Of course, we’re going to want to synch up with the version of the Android platform that your project is using. Take a look at these links:

Downloading The Android Source Tree – shows you how to get source code onto your machine.  The source lives at kernel.org here.

A List of Android Version Numbers and Names – you will need these to check out the code properly

Here is the command I used to fetch the source code for Android 2.1 (eclaire):


$ cd Android-2.1-eclair/
$ ls -als
$ pwd
$ repo init -u git://android.git.kernel.org/platform/manifest.git -b eclair
$ repo sync

…. lots of output follows ….

Checking out files: 100% (9063/9063), done.out files:   1% (129/9063)
Checking out files: 100% (8503/8503), done.out files:   0% (12/8503)
Checking out files: 100% (429/429), done.g out files:  29% (126/429)
Checking out files: 100% (599/599), done.ng out files:  42% (257/599)
Checking out files: 100% (1959/1959), done. out files:   2% (53/1959)
Checking out files: 100% (1233/1233), done.
Checking out files: 100% (620/620), done.ng out files:  29% (181/620)
Checking out files: 100% (920/920), done.ng out files:  12% (114/920)
Syncing work tree: 100% (162/162), done.

$

Once you’ve checked out the source, take a look at your project in Eclipse.  Associate the Android .jar file with

More to Come …