Peter Torr has opened a can of worms by requesting feedback on Tombstoning within Windows Phone 7. Conceptually this isn’t a particularly difficult concept – when your application goes into the background there is a chance that the operating system will terminate the process in order to reclaim system resources. Unfortunately the current implementation in Windows Phone 7 has lead to a lot of confusion. Developers don’t know when their application is going to be terminated, restarted, suspended or resumed. As such they’re using trial and error to attempt to predict when their application is going to be terminated. Let’s start with a couple of examples to illustrate this point.
Scenario 1: Application is not terminated
– User starts an application
– User clicks the start button
– User clicks the back button, returning to the same instance of the application
Scenario 2: Application is terminated (tombstoned) and then restarted
– User starts an application
– User clicks the start button
– User clicks on another application (this will terminate the running instance of the first application)
– User clicks the back button (this will terminate the second application, returning the user to Start)
– User clicks the back button again (this will launch a new instance of the first application)
At the moment the behaviour of WP7 is fairly deterministic, which means that with enough trial and error you can determine all of the scenarios under which your application will be tombstoned or not. However, the premise of the model is that your application could be tombstoned at any stage whilst it is in the background to allow the operaing system to reclaim resources. I think it was one of the earliest CTPs of WP7 that actually behaved this way and it was only in the beta where a more deterministic model was imposed. The upshot of this is that you need to anticipate that your application will be tombstoned when it goes into the background. This is NOT to say that you should assume it has been tombstoned, you just need to code for the case where it is tombstoned.
Ok, so how do we deal with tombstoning…. Again, Peter Torr has a couple of things to say about this in his post on handling activating and deactivating events. In this post Peter discusses the different sequence of events that happens when an application is tombstoned v’s the sequence that happens when it is not. Let me reproduce them again here for your pleasure:
Tombstone Case (typical)
- Current page gets OnNavigatedFrom
- Application gets Deactivated
- Process dies
- Process starts
- Application gets constructed
- Application gets Activated
- Current page gets constructed
- Current page gets OnNavigatedTo
Non-Tombstone Case (Start -> Back)
- Current page gets OnNavigatedFrom
- Application gets Deactivated
- Application gets Activated
- Current page gets OnNavigatedTo
Now, since you want to be able to handle both scenarios it would make sense to only work with events/method calls that happen in both cases. The intersection of these two scenarios actually coincides with the Non-tombstone case where there are OnNavigatedFrom, Deactivated, Activated and OnNavigatedTo events/methods. These are the points where you should be doing ALL transient data persistence and restoration.
“Transient data”….. please define! Ok, I typically think of two different types of data within an application. There is persistent data, which is any data the user has saved or that should survive multiple instances of an app (for example a document that the user saves would be persistent data because they’d expect it to be there the next time they run the app). Then there is transient data, which is any information that the user has entered but hasn’t been saved (for example form fields that have been completed but the form hasn’t been submitted). The user would expect transient information to survive through the lifetime of the page. By this I mean that if they navigate off to another application and then return to the original app the transient data would still be on the page. Conversely if they close the page by navigating using the Back button, they’d expect that data to be lost. Similarly if they restarted the app from the Start they would not expect the data to still be there.
So this bodes the question, how do we persist transient data? If we saved it to isolated storage then it’s going to behave like persistent data. Luckily Windows Phone 7 has a solution in the form of both application and page level state dictionaries. When an application is launched these dictionaries are empty. You can populate these dictionaries with data (key value pairs) and the data will survive tombstoning. The only difference between the application level dictionary and the page level dictionaries are that the page dictionaries only last the lifecycle of the page. When the use clicks the back button to close a page, the associated state dictionary is destroyed.
Ok, so that’s starting to make sense, but where’s the best place to save transient state? The answer to this is based on the lifecycle of the data itself. You may have data that is used application wide, in this case you’ll want to persist and restore this data in the application state dictionary in the Deactivated and Activated events. Alternatively, any data that is page specific should be persisted and restored in the state dictionary for that page in the OnNavigatedFrom and OnNavigatedTo methods.
Example time…..
Application Wide Transient State
public string ApplicationWideData { get; set; }
private void Application_Activated(object sender, ActivatedEventArgs e)
{
object stateData;
if (PhoneApplicationService.Current.State.TryGetValue("AppWideData", out stateData))
{
ApplicationWideData = stateData as string;
}
}
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
Debug.WriteLine("Application Deactivated");
PhoneApplicationService.Current.State["AppWideData"] = ApplicationWideData;
}
Page Transient State
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
this.State["PageData"] = PageData;
Debug.WriteLine("(Main Page) Navigated From");
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
Debug.WriteLine("(Main Page) Navigated To");
object stateData;
if (this.State.TryGetValue("PageData", out stateData))
{
PageData = stateData as string;
}
}
In most cases you’re going to want to persist transient information about the page. You should develop pages so that they don’t rely on any page being created or loaded prior to them. Doing this, and making use of the page state dictionary, is by far the easiest way to handle the challenges associated with tombstoning.