Windows Phone 7 Quick Tutorials: Part 4 - Tombstoning and Data Persistence

In the previous articles of the series we've seen how to create simple WP7 Silverlight applications, use the app bar and navigate between different pages. In this article we'll tackle the application lifecycle and how can we store the application's settings.

Tombstoning

On Windows Phone 7 only one application can run at a time, as the system does not support multi-tasking. The reason is that multiple applications running on the background can degrade the performance. On the other hand, users can navigate forward or backwards through the stack of (previously) running applications; forward, by starting applications from the installed applications list or from a tile on Start, and backwards using the Back button.

In addition, applications can be interrupted by Launchers (APIs that WP applications can use to enable tasks such as making phone calls or sending emails, without returning any data to the calling application) or Choosers (APIs that WP applications can use to launch built-in application that allows the user to complete a task, such as taking a picture, which returns some data to the calling application).

When the user presses the Back button, the application is terminated. However, when the user presses the Start button, or a launcher or chooser starts, the application is not terminated, but put into a sort of hibernation, with the system maintaining application state; if the user returns to the application by pressing the Back button, or when the launcher or chooser terminates, the application is resurrected and the application state restored. This process is called tombstoning.

The image below shows (in green) the possible states of the application and the events fired when the state changes (in red).

State and Events

Applications usually contain data that should be preserved when deactivating or closing and restored when activated or re-started. This can be either at page level or application level:

  • Page state: is the state specific to a particular page, usually referring to the visual appearance (content of controls, position of scroll bars, etc.).
  • Application state: is the state common to the entire application and not specific to a particular page.

Page state should be managed in the following Page event handlers:

  • OnNavigatedTo: called when a page becomes the active page in a frame
  • OnNavigatedFrom: called when a page is no longer the active page in a frame

Application state should be managed in the handlers for the events exposed by the PhoneApplicationService (shown in red rectangles in the previous image):

  • Launching: occurs when the application is being launched.
  • Closing: occurs when the application is terminated as a result of pressing the Back button on the first page.
  • Activated: occurs when the application is made active after being previously tombstoned, as a result of returning to the application with the Back button or a Launcher/Chooser finishing work.
  • Deactivated: occurs when the application is tombstoned as a result of pressing the Start button or a Launcher/Chooser activating.

Preserving State

The data associated with your application can be grouped in two categories:

  • Transient: data that is reinitialized each time the application starts, but needs to be preserved when the application is tombstoned
  • Persistent: data that must be maintained across different runs; it must be saved and restored both when the application is tombstoned and completed terminated.

The framework offers two different storages for state persistence, both of them being dictionaries:

  • PhoneApplicationService.Current.State: used for transient data only; this dictionary is automatically persisted when the application is tombstoned and restored when the application is activated again, but is not saved when the application is closing. It can be only used to store data that is not required when a new instance of the application starts.
  • IsolatedStorageSettings.ApplicationSettings: used for persistent data, required between different runs of the application. Can be used both when the application is just tombstoned or completely terminated. They are stored in the isolated storage, which is an area of the disk storage that each WP7 application gets access to.

In addition to these dictionaries you can use other means to save your data, such as the types available in the System.IO.IsolatedStorage namespace: IsolatedStorageFile and IsolatedStorageFileStream. IsolatedStorageSettings is a class designed to ease access to the isolated storage. This is what we'll use in this article.

Windows Phone 7 Quick Tutorials: Part 4 - Tombstoning and Data Persistence

Building an example

To show how to manage the application lifecycle and application settings we'll build an example that backs-up and restores both page state and application state. This Silverlight application will have one single page that displays three counters:

  • total starts of the applications (persistent application state)
  • total re-activations of the application from tombstoning after the last fresh start-up (transient application state)
  • total hits of a button on the main page (transient page state)

This is how the XAML code for the content panel should look like:

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <StackPanel Grid.Row="0">
        <TextBlock Text="Total application runs"
                   HorizontalAlignment="Center" TextAlignment="Center"
                   Style="{StaticResource PhoneTextTitle2Style}"/>
        <TextBlock x:Name="totalAppRuns"  
                   Text="0"
                   HorizontalAlignment="Center"
                   Style="{StaticResource PhoneTextTitle1Style}" />
        <TextBlock Text="Total application activations since last start"
                   TextWrapping="Wrap"
                   HorizontalAlignment="Center" TextAlignment="Center"
                   Style="{StaticResource PhoneTextTitle2Style}"/>
        <TextBlock x:Name="totalAppActivations"  
                   Text="0"
                   HorizontalAlignment="Center"
                   Style="{StaticResource PhoneTextTitle1Style}" />
        <TextBlock Text="Total button hits since the last start"
                   TextWrapping="Wrap"
                   HorizontalAlignment="Center" TextAlignment="Center"
                   Style="{StaticResource PhoneTextTitle2Style}"/>
        <TextBlock x:Name="totalButtonHits"  
                   Text="0"
                   HorizontalAlignment="Center"                           
                   Style="{StaticResource PhoneTextTitle1Style}" />
        <Button x:Name="btnHit" 
                Content="Hit me"
                Click="btnHit_Click"/>
    </StackPanel>
</Grid>

And this is how the page looks in the designer:

[wp7tombstoning_3.png]

Preserving page state

When pressing the button labeled "Hit me" an internal page counter is incremented and the value is displayed on the page. However, when the application is tombstoned we want to save that counter and restore it back when the application is reactivated and the page is navigated to again. For that we will override the OnNavigatedFrom handler to store the hit count in the PhoneApplicationService.Current.State dictionary, and the OnNavigatedTo handler to retrieve the hit count from the transient dictionary and display it in the page. The code for the main page looks like this:

public partial class MainPage : PhoneApplicationPage
{
    int totalHits = 0;

    public MainPage()
    {
        InitializeComponent();
    }

    private void btnHit_Click(object sender, RoutedEventArgs e)
    {
        totalHits++;
        totalButtonHits.Text = totalHits.ToString();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        object obj;
        if (PhoneApplicationService.Current.State.TryGetValue("totalHits", out obj))
        {
            totalHits = (int)obj;
            totalButtonHits.Text = totalHits.ToString();
        }

        base.OnNavigatedTo(e);
    }

    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        PhoneApplicationService.Current.State["totalHits"] = totalHits;

        base.OnNavigatedFrom(e);
    }
}

Preserving application state

If you look in the App.xaml page you'll notice the following event handlers for the events of the PhoneApplicationService class.

<Application.ApplicationLifetimeObjects>
    <!--Required object that handles lifetime events for the application-->
    <shell:PhoneApplicationService 
        Launching="Application_Launching" Closing="Application_Closing" 
        Activated="Application_Activated" Deactivated="Application_Deactivated"/>
</Application.ApplicationLifetimeObjects>

They are implemented as empty methods in the App class, with comments indicating when the handlers are called.

// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
}

// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
}

// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
}

// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
}

Because you can't be sure that a tombstoned application will actually be re-activated, when the application is deactivating you should store both transient and persistent data and reload it appropriately when activating. You should adhere to the following pattern for saving and loading application data:

  • Application_Launching: load only the persistent data (required between different runs)
  • Application_Closing: save only the persistent data
  • Application_Activated: load both persistent data and transient data
  • Application_Deactivated: save both persistent data and transient data

Let's consider that we store the count of total runs and total activations since the last run in two public (read only) properties of the App class (which makes them available to all the pages of the application).

public int TotalRuns { get; private set; }
public int TotalActivations { get; private set; }

We'll have two methods for saving and two for loading application data, one pair for the persistent data and one for the transient data. The persistent data is the total number of runs of the application, and the transient data is the total number of activations since the last run. These four methods will look like this (they should be self-explanatory by now):

private void SaveTransientData()
{
    PhoneApplicationService.Current.State["totalActivations"] = TotalActivations;
}

private void LoadTransientData()
{
    object obj;
    if (PhoneApplicationService.Current.State.TryGetValue("totalActivations", out obj))
    {
        TotalActivations = (int)obj;
    }
}

private void SavePersistantData()
{
    if (IsolatedStorageSettings.ApplicationSettings.Contains("totalRuns"))
    {
        IsolatedStorageSettings.ApplicationSettings["totalRuns"] = TotalRuns;
    }
    else
    {
        IsolatedStorageSettings.ApplicationSettings.Add("totalRuns", TotalRuns);
    }
    
    // make sure data is saved immediatelly
    IsolatedStorageSettings.ApplicationSettings.Save();
}

private void LoadPersistantData()
{
    if (IsolatedStorageSettings.ApplicationSettings.Contains("totalRuns"))
    {
        TotalRuns = (int)IsolatedStorageSettings.ApplicationSettings["totalRuns"];
    }
}

Following the pattern described earlier will call them from the proper event handlers:

// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
    LoadPersistantData();
    TotalRuns++;
}

// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
    LoadPersistantData();
    LoadTransientData();
    TotalActivations++;
}

// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
    SavePersistantData();
    SaveTransientData();
}

// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
    SavePersistantData();
}

Notice that the total runs counter is only incremented in the handler for the Launching event after loading the persisting data, and the total activations counter is only incremented in the handler for the Activated event after loading the transient data.

In order to display these counters in the main page, we'll need the following addition to the OnNavigatedTo method of the main page (the only page of the application):

// application settings
App app = Application.Current as App;
totalAppActivations.Text = app.TotalActivations.ToString();
totalAppRuns.Text = app.TotalRuns.ToString();

Examples

The first set of images below shows:

  • The counters after starting the application the first time: runs=1, activations=0, button hits=0
  • The counters after pressing the button three times: runs=1, activations=0, button hits=3
  • The counters after pressing the Start button to tombstone the application and then the Back button to return to it and activated it: runs=1, activations=1, button hits=3
[wp7tombstoning_4.png]

The second set of images below shows:

  • The counters after pressing the button two more times: runs=1, activations=1, button hits=5
  • The counters after pressing the Start button to tombstone the application and then the Back button to return to it and activated it: runs=1, activations=2, button hits=3
  • The counters after pressing the Back button to close the application and then launching the application again: runs=2, activations=0, button hits=0
[wp7tombstoning_5.png]

Conclusion

WP7 applications can be in different states, they can be deactivated and reactivated by the system as user navigates to and away the application or launchers/choosers activate. The application data (whether at page level or application level) should be backed-up and restored. There are different dictionaries the framework offers for this purpose but developers can employ other means too. This article discussed the application lifecycle, the tombstoning and data persistence.



About the Author

Marius Bancila

Marius Bancila is a Microsoft MVP for VC++. He works as a software developer for a Norwegian-based company. He is mainly focused on building desktop applications with MFC and VC#. He keeps a blog at www.mariusbancila.ro/blog, focused on Windows programming. He is the co-founder of codexpert.ro, a community for Romanian C++/VC++ programmers.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

  • The latest release of SugarCRM's flagship product gives users new tools to build extraordinary customer relationships. Read an in-depth analysis of SugarCRM's enhanced ability to help companies execute their customer-facing initiatives from Ovum, a leading technology research firm.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds