Wednesday, June 27, 2012

Process Lifetime Management (PLM) and Managing State in Windows 8 Metro C# Applications

In the traditional Windows environment, the user manages the lifetime of their applications. The user launches the application and it continues to run until the user decides to close it. The problem with this model is that applications continue to drain system resources, including memory and CPU, even when they are not in the foreground. This impacts the performance of the application the user is using as well as drains the battery faster when the device is not plugged in.

Windows 8 Metro applications only run when they are in the foreground. This allows the user to focus on the primary application they wish to interact with. Applications in the background go into a suspended state. In the suspended state the threads for the application are literally frozen in place. The application will no longer take up system resources or impact battery life. Most of the time, the application will remain in memory. This allows fast application switching, so when the user swipes back to a different application, it resumes immediately as if it were already running.

There is a case where the background application may be terminated by the system. This is based on system resources. For example, if the foreground application requires a large chunk of memory that will potentially result in a low memory state, the system may terminate one or more applications that are in the suspended state. It will target background applications based on their memory consumption and terminate the ones taking up the most resources first.

This is very easy to emulate. From Visual Studio 2012, create a new Windows 8 Metro Project for C# using the Grid template:

Now launch the application in debug mode. It is easier to do this if you have dual monitors so that you can debug from one monitor and work with the Metro application on the second; other options include launching into the simulator and remote debugging to another device. Once the application is running, navigate to a few different pages. In Visual Studio 2012, choose the "Suspend and Shutdown" option.

You've just simulated the termination event. For many users, this will happen without them knowing when an application is in the background. When the user returns to the application, however, they will expect to continue where they left the application. You can simulate this by debugging and launching the application again. You'll find that you end up on the same page you left, and even the navigation history (i.e. the "Back" button) is retained. How is this possible?

Here is a rough visualization of Process Lifetime Management in Windows 8 Metro applications:

To understand how you are able to preserve state in your application, it's also important to understand navigation. Unless you are using a custom form of navigation, you are using the built-in Frame class to manage your navigation. This class provides navigation services, as well as a place to host the target pages as they are loaded. In general, a navigation sequence looks something like this:

The navigation events are important because they provide a way to hook into the pipeline and are the logical way to preserve state. In the Visual Studio 2012 templates provided for Metro applications, a bit of magic is created in the form of two important classes. First, if you inspect the pages of your application you will find they derive from the class LayoutAwarePage. This page handles a lot of housekeeping work for you. It will respond to orientation changes by updating the visual state of the page, and it handles navigation in conjunction with the second important class, SuspensionManager.

If you open the LayoutAwarePage source code, you will find a region for Process Lifetime Management. In that region, you will find the navigation events are overridden. Within these methods is code that manages the state of the application for you. It does this by creating a dictionary for each page that saves, among other things, the navigation parameters that were passed to the page. If you want to use this built-in system, the only rule is that whatever you pass as a navigation parameter can be serialized. This allows the SuspensionManager class to use the DataContractSerializer to save the state for your application. To see how this works, launch the application again and navigate to a few pages, then terminate it.

Double-click the Package.appxmanifest file in the Solution Explorer and navigate to the Packaging tab. Read the package name:

Now drop to a command line and type:

cd %userprofile%\appdata\local\packages

This will take you to the local folder where information is stored for your Windows 8 Metro applications. Navigate to the directory that corresponds to the package name you found above. Within that folder, you will find another folder called LocalState. That folder contains a file named _sessionState.xml. Open that file and you'll find the serialized state for your application.

The way this mechanism works is straightforward. Before the Release Preview, navigation was handled in a way very similar to the Windows Phone: navigation events received parameters and the application responded to those parameters. With the introduction of the built-in state management, the LayoutAwarePage intercepts navigation. Instead of using navigation, you now use the LoadState and SaveState methods instead.

These are wired for you in the templates. The LayoutAwarePage handles setting up a dictionary and saving any parameters passed to the page. When a page is navigated to, the LoadState method is called. Here is an example that is generated by the default template:

protected override void LoadState(Object navigationParameter, 
    Dictionary<String, Object> pageState)
{
    var group = SampleDataSource.GetGroup((String)navigationParameter);
    this.DefaultViewModel["Group"] = group;
    this.DefaultViewModel["Items"] = group.Items;
}

Notice that the navigation parameter is automatically restored and passed in. If your application solely relies on navigation parameters, then everything is automatic. Your application will function and users can navigate deep within the application, swap out, and return without losing navigation history. But what if you want to preserve your own values outside of the navigation parameters? That is easy enough.

In the GroupedItemsPage.xaml.cs file, change the pageTitle to hold a date instead. Here is the code in the constructor:

public GroupedItemsPage()
{
    this.InitializeComponent();
    pageTitle.Text = DateTime.Now.ToString();
}

Now there are just two steps needed to preserve the date. First, LoadState should check for a saved value and restore it if present. The first time the application is launched, the dictionary is empty, so make sure the dictionary exists before checking for the value:

if (pageState != null && pageState.ContainsKey("Date"))
{
    pageTitle.Text = (String)pageState["Date"];
}

Of course, you will also need to save the value. To save it, simply override the SaveState method. You are conveniently passed the dictionary that is saved with the state for the page, so any information you wish to persist, you can simply add to that dictionary:

protected override void SaveState(Dictionary<string, object> pageState)
{
    pageState["Date"] = pageTitle.Text;
    base.SaveState(pageState);
}

And that's all there is to it! Now you can launch the application, navigate to several different pages, suspend and terminate it, then load it again. You will find the date shown on the main page does not change because it is preserved using the built-in state management features. Of course, if you have more complex types that don't serialize directly, you may need another strategy. I cover various methods for saving and restoring data, as well as serializing more complex types, in my book Designing Windows 8 Metro Applications with C# and XAML. Happy Windows 8 coding!

Jeremy Likness