Try PandaPow for Android
By date:

RSS Feed

Skip this one

When to save the state?

2010-04-24 13:47:07

An Android Activity roughly corresponds to one specific user interface. At any given time, there is only one activity with which the user interacts. An application often has several activities, one which is the main activity and from which the others are launched. An activity should typically be able to save its state so that it can be paused and late resumed.

The android documentation gives a good overview of the activity life cycle. It is pretty much summed up by the below state diagram:

This state diagram seems pretty straight forward to understand, but there is an important detail which is easily overlooked and needs to be emphasized:

There is nothing that ensures that an instance of an activity is destroyed before another instance of the same activity is created.

In other words, there is no guarantee that onDestroy() is called on an old instance prior to onCreate() is called on a new instance.

Playing around with various android apps it is easy to be mislead into thinking that ther is such a guarantee. An activity is created when it is first launched. Then pauses when some other activity is launched on top of it, or the home button is used to switch to another app. When we return to a paused app, or back to a previous activity of the same app, it will be resumed as we left it. And when we exit an activity (e.g. pushing the back button) it will be destroyed.

In the app I was writing, I wanted the user to always return to the same state regardless of whether the app had been paused and resumed, or it had been closed using the back button and then relaunched. Looking at the activity life cycle diagram above, it then seemed appropriate to save the state in onDestroy() and restore it again in onCreate(). That however was a mistake.

While developing the app it worked fine to save the state in onDestroy(). But later when testing the app more thoroughly, the state wasn't always restored properly. It wasn't consistent either, sometimes it worked sometimes not depending on what other apps had been launched in between closing and relaunching. Using the debugger, the problem soon became clear: onDestroy() wasn't called when the app was closed, as expected. Instead a second instance of the main activity was created before the first instance could save its state.

Update: The behavior shown below is not normal to Android. It was in fact due to a static reference to the activity that wasn't cleared, and which prevented the system from destroying the activity (see here for more details ). Once this reference was cleared, the activity was indeed destroyed as expected. This doesn't mean one should count on it, however, so it is still "wrong" to save the state in onDestroy.

The problem can be illustrated by the following sequences. The first column is the expected sequence of events, the second column shows what actually happened.

Expected Actual
App launched
inst1.onCreate() loads initial state
App closed
inst1.onPause()
inst1.onDestroy() saves the state 1
App launched
inst2.onCreate() restores state 1
App closed
inst2.onPause()
inst2.onDestroy() saves state 2
App launched
inst3.onCreate() restores state 2
...
App launched
inst1.onCreate() loads initial state
App closed
inst1.onPause()
App launched
inst2.onCreate() no state to restore
inst1.onDestroy() saves the state 1
App closed
inst2.onPause()
App launched
inst3.onCreate() restores state 1
inst2.onDestroy() saves state 2
...

Looking at the two sequences, the it was quite clear how to fix it. The save of state should be done in onPause() instead. The following compares the sequence before and after the fix.

Before Fix After Fix
App launched
inst1.onCreate() loads initial state
App closed
inst1.onPause()
App launched
inst2.onCreate() no state to restore
inst1.onDestroy() saves the state 1
App closed
inst2.onPause()
App launched
inst3.onCreate() restores state 1
inst2.onDestroy() saves state 2
...
App launched
inst1.onCreate() loads initial state
App closed
inst1.onPause() saves the state 1
App launched
inst2.onCreate() restores state 1
inst1.onDestroy()
App closed
inst2.onPause() saves state 2
App launched
inst3.onCreate() restores state 2
inst2.onDestroy()
...

In hindsight this "fix" seems pretty obvious; all examples and tutorials suggest that saving of state should be done in onSaveInstanceState() which is always called immediately before onPause(). The reason this wasn't done in the first place was:

  1. The onSaveInstaceState() is meant for saving and restoring state in the case the instance is destroyed by the system, not if it is closed normally by the user.
  2. Saving state was potentially time consuming, so it seemed a bad idea to save state when just pausing. If the activity was later resumed without being destroyed, then the saved state would never be used.

For efficiency, it would of course be better if one could save the sate, only when it is necessary. But on the other hand, with the current solution, an application is forced to keep the state simple so as to not need time to save. That seems like a good direction to go in anyway.

Page 1