Windows Phone: Handling the Lifecycle at Two Levels

Application-level and page-level state management for Windows Phone tombstoning, with a full isolated-storage sample.

The sudden closure of your software may trigger a series of issues if you don't handle these situations correctly. To make the user experience similar to what real multithreading would provide, it is important to handle lifecycle events at two levels: the application level and the page level.

Application Level State Management

The application level includes the state of the entire application, including elements not directly related to the visual page aspect. For example, if you have a service running in a background thread with application-wide state, you need to save this before tombstoning.

The runtime exposes four events during tombstoning phases, startup, and closing:

// 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)
{
}

These handlers are hooked up in the App.xaml.cs file. The source of the events is the PhoneApplicationService instance present inside the LifetimeObjects section.

You can save state using the PhoneApplicationService.Current.State dictionary, which is retained in memory when the application is tombstoned but not when it is definitively closed.

Correct State Handling Process

  1. In the deactivating event: Save information into the transient "State" dictionary that can be lost if the application doesn't restart. For sensitive data you want saved even after complete restarts, use persistent storage like IsolatedStorage.

  2. In the activating event: Read back the saved information and recreate the exact state. The user should find exactly what they left during tombstoning.

  3. In the closing event: Save required information to persistent storage. The deactivating and closing events are mutually exclusive.

  4. During the launching event: Restore only data saved in the closing event. This event means the user restarted the application from scratch and expects to find it in its initial state. This is mutually exclusive with the activating event.

Sample Implementation

For an application that pulls data from a WCF service periodically with an "Items" collection (saved across restarts) and a "Current" property (saved only during tombstoning):

// 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)
{
    MyApplicationState.Instance.Items = this.LoadFromIsolatedStorage();
    MyApplicationState.Instance.Current = null;
}

// 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)
{
    MyApplicationState.Instance.Items = this.LoadFromIsolatedStorage();

    if (PhoneApplicationService.Current.State.ContainsKey("CurrentItem"))
    {
        MyApplicationState.Instance.Current = PhoneApplicationService.Current.State["CurrentItem"] as ItemData;
        PhoneApplicationService.Current.State.Remove("CurrentItem");
    }
    else
        MyApplicationState.Instance.Current = null;
}

// 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)
{
    this.SaveToIsolatedStorage(MyApplicationState.Instance.Items);
    PhoneApplicationService.Current.State["CurrentItem"] = MyApplicationState.Instance.Current;
}

// 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)
{
    this.SaveToIsolatedStorage(MyApplicationState.Instance.Items);
}

private List LoadFromIsolatedStorage()
{
    using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
    {
        if (file.FileExists("app.state"))
        {
            using (IsolatedStorageFileStream stream = file.OpenFile("app.state", System.IO.FileMode.Open))
                return this.DeserializeXml(stream);
        }

        return new List();
    }
}

private List DeserializeXml(IsolatedStorageFileStream stream)
{
    XmlSerializer serializer = new XmlSerializer(typeof(List));
    return serializer.Deserialize(stream) as List;
}

private void SaveToIsolatedStorage(List state)
{
    using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
    {
        using (IsolatedStorageFileStream stream = file.OpenFile("app.state", System.IO.FileMode.Create))
            this.SerializeXml(stream, state);
    }
}

private void SerializeXml(IsolatedStorageFileStream stream, List state)
{
    XmlSerializer serializer = new XmlSerializer(typeof(List));
    serializer.Serialize(stream, state);
}

Page Level State Management

Application-level handling is insufficient. Many details must be managed at the page level during tombstoning — such as form data, game level, or scroll position. These details only apply when tombstoning occurs while a page is open.

A state bag is available through a page property. Unfortunately, activating and deactivating events cannot be handled at the page level because the activating event is raised long before the page instance is recreated during restoration after tombstoning.

To handle tombstoning at the page level, use navigation events. The OnNavigatedTo event is raised during the tombstoning process with an empty parameter. The NavigationEventArgs.Content property (which normally indicates the navigation target) is empty when navigation exits the application scope, which occurs only during tombstoning.

Here's how to persist textbox state:

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    if (this.State.ContainsKey("Message"))
    {
        this.message.Text = this.State["Message"] as string;
        this.State.Remove("Message");
    }

    base.OnNavigatedTo(e);
}

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
    if (e.Content == null) // TOMBSTONING
    {
        State.Add("Message", message.Text);
    }

    base.OnNavigatedFrom(e);
}

This code persists the message textbox content and retrieves it after restoration.

See also: Windows Phone 7: Application Life Cycle.