Chapter 3. Fast application switching and resume

This chapter covers

  • Fast application switching
  • Responding to lifetime events
  • Fast application resume

Like any other operating system, an application on the Windows Phone starts up, runs for a while, and in the normal course of things, eventually exits. In other multitasking operating systems, an application can be moved to the background when the user switches applications. While in the background, the application will continue to run in lower-priority time slices. The Windows Phone OS is a multitasking operating system, but puts limitations on background operations. When a user switches applications, the running application is paused, and system resources are disconnected from the process so they can be freed up for foreground applications. A dormant application might eventually be terminated if the operating system needs to allocate additional resources to the foreground application.

Applications can’t run in the background, but Windows Phone offers several options for the developer to build applications that require multitasking features. Fast application switching and fast application resume provide dormant applications the tools for quickly returning to full operation, giving the user the impression that the application continued to run. Background agents, covered in chapter 4, provide the mechanisms applications use to perform tasks even when applications aren’t running.

The multitasking limitations imposed on applications may seem severe to developers looking to build the next killer mobile application. Microsoft has imposed these limitations to ensure that the user has the best overall experience possible. Background tasks aren’t allowed to affect or slow down foreground applications, nor are they allowed to perform tasks that would quickly drain the phone’s battery.

In this chapter you’ll create a sample application that demonstrates how to build applications that support fast application switching and resume.

3.1. Fast application switching

Fast application switching is the term coined by Microsoft’s Windows Phone team to describe the process that pauses a running application when the user switches to another application. The paused application usually remains in memory but can’t execute any code. The application will resume running when the user switches back to the application, making it the foreground application once again.

An application may be paused via one of many scenarios:

  • The user presses the Windows or the Search button.
  • The user chooses to reply to a newly received text message.
  • The user chooses to respond to a newly received toast notification.
  • The user presses the Camera button.
  • The user locks the phone, or the idle timeout expires.
  • The application shows a launcher or chooser.

When the user backs out of the application, it’s shut down normally. In all other situations, the user can restart an application with the Back button or by selecting it with the Task Switcher UI. If the application supports fast application resume (which we cover later in this chapter), an application can even be restarted from the Start Screen or Application List. Upon restart, the application should return to the state it was in before being paused, giving the user the impression that the application continued to run.

An application can also be obscured. Obscuration occurs when the operating system covers part of an application in favor of another application’s UI. An application may be obscured when any of the following happens:

  • The user receives a new phone call.
  • The user returns to a phone call that was active before an application started.
  • The user receives a new toast notification.
  • The Lock Screen is enabled and the application has disabled idle detection mode.
  • An alarm or reminder is triggered.
  • An application uses the PhoneCallTask launcher.
  • The user activates the Task Switcher.

Pausing and obscuration interfere with the normal flow of an application. Your application should be designed to react to interruptions by appropriately responding to the application lifetime events.

3.1.1. Understanding lifetime events

The Windows Phone class libraries expose operating system lifetime events, with several events defined on a few different classes. Table 3.1 describes each of the lifetime events and where the event is implemented in the framework.

Table 3.1. Application lifetime events

Event

Purpose

Implementation

Launching Notifies the application that it’s being started from scratch PhoneApplicationService.Launching
Closing Notifies the application that it’s being terminated as part of a normal application exit PhoneApplicationService.Closing
Activated Notifies the application that it’s being resumed from a dormant or tombstoned state PhoneApplicationService.Activated
Deactivated Notifies the application that it’s being paused to allow another process to execute PhoneApplicationService.Deactivated
Obscured Notifies the application that it’s losing focus in favor of an overlay PhoneApplicationFrame.Obscured
Unobscured Notifies the application that it’s regaining focus PhoneApplicationFrame.Unobscured

An application begins life when the user taps the application’s icon in the Application List. The application host constructs an instance of the Application class, raises the Launching event, and then constructs and navigates to the default Navigation page. At this point the application is running, solving user problems, and generally making life easier. In the normal course of activity, the user presses the Back button to exit the application, and then the operating system navigates away from the application and raises the Closing event. The launching-running-closing workflow can be seen in figure 3.1 as the straight-line path between Start and End.

Figure 3.1. Application lifetime states and the actions that trigger state transition

While running, an application will likely receive other lifetime events. When the Obscured event is raised, the application transitions to an obscured state. The application remains running, but its user interface is partially or completely hidden. When the user interface is uncovered, the Unobscured event is raised, and the application returns to the normal running state.

Deactivation is triggered when the operating system pauses an application in order to execute another process. The Deactivated event is raised to notify the application that it’s transitioning to the dormant state. In the dormant state, the application remains in memory, with all processing and threads stopped, to facilitate a fast restart when the user switches between applications. When the user returns to the application, the Activated event is raised, and the application returns to the normal running state.

A dormant application might also transition to a tombstoned state. When an application is tombstoned, no notification is given, the application is removed from memory, and almost all of its resources are freed up for other applications to use. The only application resources that are maintained in memory are those items that have been stored in one of two State dictionaries provided by the Windows Phone framework specifically for tombstoning scenarios. When the user navigates back to the application, the application host constructs a new instance of the Application class, raises the Activated event, and then constructs and navigates to the previously focused page. It’s the application’s responsibility to save application data to the State dictionaries and to read and restore data after reactivation.

When an application is dormant or tombstoned, the operating system may decide to terminate the application and end its life. When terminated, the application and any saved state are removed from memory. An application isn’t notified when being transitioned to the end state.

In this chapter you’ll build an application that handles each of the lifetime events. You’ll add code to handle the Launching, Activated, Deactivated, Obscured, and Unobscured events. You’ll start your sample application in the usual way—by creating a new project.

3.1.2. Creating the Lifetime sample application

The Lifetime sample application will handle each of the lifetime events. The sample code demonstrates how to distinguish between returning from a dormant state and a tombstoned state. You’ll learn how to store application data in the State dictionaries and how to recover the data when returning from a tombstoned state. Finally, we show how to design an application capable of running when the phone is locked.

The Lifetime application records the time when each of the lifetime events is raised, displaying the times on the screen. Moving beyond the lifetime events, we explore other interesting moments in the Lifetime application such as construction and navigation. Figure 3.2 shows the Lifetime sample application.

Figure 3.2. The Lifetime sample application

Create a new project using the Windows Phone App template and name the project Lifetime. The Visual Studio Windows Phone App project template automatically creates lifetime event handlers for the PhoneApplicationService events in App.xaml.cs. Events handlers aren’t created automatically for the PhoneApplicationFrame events. A PhoneApplicationService instance is declared in App.xaml, and the Launching, Closing, Activated, and Deactivated events are wired up to methods generated in App.xaml.cs. The following listing shows the App.xaml markup and the corresponding event handlers in App.xaml.cs.

Listing 3.1. Generated application lifetime event handlers
<Application.ApplicationLifetimeObjects>
    <shell:PhoneApplicationService
        Launching="Application_Launching"
        Closing="Application_Closing"
        Activated="Application_Activated"
        Deactivated="Application_Deactivated"/>
</Application.ApplicationLifetimeObjects>

private void Application_Launching(object sender, LaunchingEventArgs e){}
private void Application_Activated(object sender, ActivatedEventArgs e){}
private void Application_Deactivated(object sender,
    DeactivatedEventArgs e){}
private void Application_Closing(object sender, ClosingEventArgs e){}

An application isn’t considered correct if PhoneApplicationService events aren’t handled. The handlers must be both declared in the XAML and implemented in the code-behind, at least as generated by the Visual Studio wizard.

The main page for the Lifetime sample application displays how many seconds ago a particular lifetime event occurred. The sample application will record the occurrence time of each of the lifetime events, along with a few other interesting events. The sample application displays these times as the number of seconds since the event was raised. The information for each event is displayed with two TextBlocks, the first displaying a label and the second displaying the number of seconds since the event. To organize all the controls, you’ll divide the ContentPanel into several rows and columns, as shown in the following listing.

Listing 3.2. Defining the ContentPanel’s layout

First, you divide the Grid into 11 rows and two columns . We’ve omitted the declaration of most of the rows for the sake of space; the rows not shown should be assigned a Height of 30 just like the first row. Whereas most of the rows and the first column are assigned a fixed height or width, the last row and last column fill the remainder of the page’s available space.

As you work through the chapter, you’ll add several TextBlock controls to MainPage.xaml. You’ll add the first few controls in the next section when you display the number of seconds elapsed since the MainPage was constructed. Because the number of seconds continuously changes, the application should update the user interface once every second.

3.1.3. Updating the user interface

The sample application will use a DispatchTimer to update the user interface once every second. A DispatcherTimer raises a Tick event once per a specified time interval. The DispatcherTimer executes the event handler on the UI thread, making it ideal for updating the user interface. Add the code in the following listing to MainPage.xaml.

Listing 3.3. Using a DispatcherTimer to update the user interface

The DispatcherTimer is constructed in the MainPage constructor and configured to raise the Tick event once per second . The timer_Tick event handler calls a method named UpdateUserInterface, which is responsible for updating the user. You’ll add code to the UpdateUserInterface method as you progress through the chapter.

With the sample application project created and the initial framework in place, you’re ready to start investigating each of the lifetime events, starting with the Launching event.

3.2. Launching the application

You learned about application startup in chapter 2. During normal application startup, an instance of App and an instance of MainPage are constructed, and the application host calls the NavigationService directly to navigate to MainPage.xaml. In addition to constructing these two objects, the PhoneApplicationService raises the Launching event. In this section we’ll look at how to detect when an application is launched.

3.2.1. Construction

The sample application captures when the App and MainPage instances are constructed and shows the times in the user interface. The construction times are stored in properties in each of the two classes. You’ll start the implementation by updating the class in App.xaml.cs to define and assign a property named AppConstructedTime. The property is defined as an automatic property with a private setter. The App-Constructed-Time property is assigned to the current time in the App class constructor:

public DateTime AppConstructedTime { get; private set; }
public App()
{
    AppConstructedTime = DateTime.Now;
    ...
}

Next, add a similar property to the MainPage class. In addition to recording the construction time, record when the OnNavigatedTo method is called. These two times are stored in two fields named pageConstructedTime and navigatedToTime:

DateTime pageConstructedTime;
DateTime navigatedToTime;

Now, update the MainPage class constructor to assign the current time to the pageConstructedTime field:

public MainPage()
{
    InitializeComponent();
    pageConstructedTime = DateTime.Now;
...
}

The navigatedToTime field is assigned the current time in the overridden On-NavigatedTo method:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    navigatedToTime = DateTime.Now;
    base.OnNavigatedTo(e);
}

Remember that the timer runs once a second and calls the UpdateUserInterface method. The UpdateUserInterface method is responsible for updating the user interface with the time values stored in the App properties and the MainPage fields. UpdateUserInterface sets the time values into associated TextBlock controls. Before you edit the UpdateUserInterface method, you need to add a few controls to MainPage.xaml, as shown in the following listing.

Listing 3.4. UI controls showing construction times
<TextBlock Grid.Row="0" Text="Application constructed:" />
<TextBlock x:Name="appConstructed" Grid.Row="0" Grid.Column="1"
    Text="{Binding StringFormat='{0:N0} seconds ago'}" />
<TextBlock Grid.Row="5" Text="Page constructed:" />
<TextBlock x:Name="pageConstructed" Grid.Row="5" Grid.Column="1"
    Text="{Binding StringFormat='{0:N0} seconds ago'}" />
<TextBlock Grid.Row="6" Text="Page navigated to:" />
<TextBlock x:Name="navigatedTo" Grid.Row="6" Grid.Column="1"
    Text="{Binding StringFormat='{0:N0} seconds ago'}" />

In this code listing, you’ve added six TextBlock controls. Three of the TextBlocks are used as labels. The other three display the relevant elapsed time values and are named appConstructed, pageConstructed, and navigatedTo. These three controls are used by the UpdateUserInterface method:

public void UpdateUserInterface()
{
  DateTime now = DateTime.Now;
  pageConstructed.DataContext = (now - pageConstructedTime).TotalSeconds;
  navigatedTo.DataContext = (now - navigatedToTime).TotalSeconds;
  var app = (App)Application.Current;
  appConstructed.DataContext = (now - app.AppConstructedTime).TotalSeconds;
}

The DataContext properties of the TextBlock controls are assigned values calculated by the number of seconds elapsed between the current time and the time stored in the related variables in the MainPage and App classes.

Run the application and examine the values displayed on the screen. On first run, the screen should display construction and navigation times of 1 or 0 seconds ago. The left side of figure 3.3 shows the initial construction times. Press the Start button and launch another application. Using the Task Switcher or the Back button, switch back to the running instance of the sample application.

Figure 3.3. Lifetime application after initial construction (left) and restart (right)

Note

The Task Switcher is launched in the emulator by pressing and holding the F1 key on your computer keyboard. If you’re running the application on a real device, press and hold the Back button until the Task Switcher is shown.

Windows Phone maintained the application process in memory, and the App and MainPage classes don’t need to be recreated when the application is restored. Once the sample application is restored and running, the screen should display construction times of several seconds ago, depending on how long you took to switch back to the application. The navigation time should reset to 0 again, because OnNavigatedTo is called when returning to the application. The right side of figure 3.3 shows the user interface after the restart.

In this section you added code to track when the App and MainPage classes are constructed. The classes are constructed when the application is first launched and aren’t necessarily constructed when the application returns from dormancy. Later in the chapter, we show situations where the operating system tombstones an application, and the App and MainPage classes are constructed again when the application is reactivated.

Fortunately, the Windows Phone framework provides the lifetime events to determine when an application is created during initial launch and when the application classes are re-created upon application reactivation.

3.2.2. First-time initialization

When a user taps an application’s icon on the phone’s Application List, a new instance of the application is launched. If an existing instance of the application is currently dormant, it’s removed in favor of the new instance. Other triggers that create a new instance of an application include the user tapping an application or secondary tile in the Start Screen, tapping the details section of an alarm or reminder, and using the hub extension points. (See chapter 4 for details on alarms and reminders and chapter 9 for hub extension points.) Later in the chapter, you’ll learn how to use fast application resume to prevent a dormant application from being replaced by a new instance.

The first time an application instance runs, the Launching event is raised. When a dormant application resumes, the Launching event isn’t raised. Because the Launching event is raised only once in an application’s lifetime, the Launching event handler makes an ideal method for performing application initialization tasks.

Note

“App certification requirements for Windows Phone” (http://mng.bz/Fefo) requires that the first page be shown in less than 5 seconds and must be responsive to user input in less than 20 seconds.

The Lifetime sample application doesn’t have any initialization requirements other than to record the time when the Launching event is raised, which you capture in another automatic property of the App class. Open the App.xaml.cs file and add a new LaunchedTime property:

public DateTime LaunchedTime { get; private set; }

You assign the property’s value in the Launching event handler. You learned earlier in this chapter that the event handler was automatically generated with the name Application_Launching by the project template. Find the generated method and assign the current time to the LaunchedTime property:

private void Application_Launching(object sender, LaunchingEventArgs e)
{
    LaunchedTime = DateTime.Now;
}

The Application_Launching method is where you might put additional code that should only run once in the lifetime of an application. Be careful when performing tasks inside the Launching event handler. The Launching event is raised before the MainPage is created and initialized. Work performed in the event handler increases the amount of time before the application is ready to receive user input. If the delay is too long, the operating system will terminate the application.

Now that you have the launch time recorded, you can update MainPage to display the value. Add another two TextBlock controls to MainPage.xaml:

<TextBlock Grid.Row="1" Text="Application launched:" />
<TextBlock x:Name="launched" Grid.Row="1" Grid.Column="1"
    Text="{Binding StringFormat='{0:N0} seconds ago'}" />

Add a line to UpdateUserInterface to copy the value from the App property to the TextBlock control named launched:

launched.DataContext = (now - app.LaunchedTime).TotalSeconds;

With this code in place, run the application. You have a new line in the user interface showing the launch time. At this point, showing the launch time doesn’t seem interesting. The launch time is nearly exactly the same value as the construction times. When we discuss tombstoned applications later in the chapter, you’ll see situations where the launch time is greater than the construction times.

Before we can discuss tombstoning, we need to talk about what happens when the operating system deactivates, and later reactivates, an application.

3.3. Switching applications

When the user leaves an application using the Start button, the operating system pauses the application, putting the application’s process into a dormant state. Other actions that cause an application to go dormant include using launchers and choosers, tapping a notification, or using the Task Switcher to change to another application.

There are situations where a dormant application is terminated but remains on the Task Switcher’s back stack. This process is called tombstoning. When the application enters a tombstoned state, its physical process is terminated to save system resources, but its state is maintained by the operating system in volatile memory. Powering off the device or exhausting the battery has the effect of losing all the states maintained in the memory.

In this section you’ll add features to the Lifetime sample application that detect and handle going dormant and being tombstoned.

3.3.1. Going dormant

When an application is dormant, the operating system stops all running threads, unhooks sensors, and stops any timers. The application is kept in memory and placed on the top of the stack of applications shown in the Task Switcher, and is accessible using the Back button. Figure 3.4 shows the Task Switcher with the Lifetime application in the middle of the stack.

Figure 3.4. The dormant Lifetime application shown in the Task Switcher

Only a limited number of applications are kept in the back stack, including the currently running application. If there are already too many applications in the back stack when a new application is started, the dormant application at the bottom of the stack is terminated. Once terminated, the application is removed from memory, and any transient data used by the application is lost. The user can no longer navigate to the dormant application.

The operating system doesn’t notify the dormant application when it’s terminated. If you have any data that must be persisted to long-term storage, you must save it before or during deactivation. We discuss local long-term storage options in chapter 7.

Adding the DeactivatedTime property

The sample application doesn’t have any data-storage requirements, other than to capture the time when the Deactivated event is raised in a property of the App class. Open the App.xaml.cs file and add a new DeactivatedTime property:

public DateTime DeactivatedTime { get; private set; }

You assign the property’s value in the Deactivated event handler that was generated by the project template. The event handler was generated with the name Application_Deactivated. Find the generated method and assign the current time to the DeactivatedTime property:

private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
    DeactivatedTime = DateTime.Now;
}

The MainPage class also offers a hook to catch when the application is losing focus. The OnNavigatingFrom and OnNavigatedFrom override methods are called even when the operating system is navigating away from a running application.

Recording the time of the navigation event

Your sample application will record when the OnNavigatedFrom method is called with a new MainPage field named navigatedFromTime:

DateTime navigatedFromTime;

Override the OnNavigatedFrom method and assign the new field with the current time. You only want to record the current time when navigating away from the application. You can determine whether the navigation target is external to your application by checking the IsNavigationInitiator property of the NavigationEventArgs parameter. The IsNavigationInitiator will be false if leaving the application:

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    if (!e.IsNavigationInitiator)
    {
        navigatedFromTime = DateTime.Now;
    }
    base.OnNavigatedFrom(e);
}

The IsNavigationInitiator will be false anytime you’re leaving the application, including when the user is closing the application using the Back button.

Displaying the deactivation and navigation times in the UI

You need to show the new times in the user interface by adding four new TextBlock controls and modifying the UpdateUserInterface method. Open MainPage.xaml and add two TextBlock labels and two TextBlock controls named deactivated and navigatedFrom:

<TextBlock Grid.Row="2" Text="Application deactivated:" />
<TextBlock x:Name="deactivated" Grid.Row="2" Grid.Column="1"
    Text="{Binding StringFormat='{0:N0} seconds ago'}" />
<TextBlock Grid.Row="7" Text="Page navigated from:" />
<TextBlock x:Name="navigatedFrom" Grid.Row="7" Grid.Column="1"
    Text="{Binding StringFormat='{0:N0} seconds ago'}" />

Modify the UpdateUserInterface method in MainPage.xaml.cs to assign the DataContext properties:

if (navigatedFromTime != DateTime.MinValue)
    navigatedFrom.DataContext = (now - navigatedFromTime).TotalSeconds;
if (app.DeactivatedTime != DateTime.MinValue)
    deactivated.DataContext = (now - app.DeactivatedTime).TotalSeconds;

In this code snippet, you check whether the time values are equal to DateTime.MinValue. DateTime.MinValue is the default value for a DateTime variable, and you don’t want to show a value on the screen if the variable still equals its default value.

Testing the application

You’re ready to run the application. At first launch you can see the new TextBlock controls, and they don’t display any values because you haven’t deactivated the application. Press the Start button, wait a few seconds, and press the Back button. When the application restarts, you should now see values displayed in the deactivated and navigatedFrom controls.

You have one more event to look at: the Activated event, which is raised when the application restarts and returns to action.

3.3.2. Returning to action

When an application is dormant, the user can return to it by tapping the Back button one or more times or by selecting the application in the Task Switcher. When a dormant application is reactivated, threads are restarted, and the PhoneApplicationService raises the Activated event. Because the application was never removed from memory, the App and MainPage instances are preserved and don’t need to be reconstructed.

Note

Microsoft recommends that a dormant application be ready for user input within one second after being reactivated.

It’s possible that a dormant application may never return to a running state. The operating system may terminate the application if other applications are started. If the user taps an application or secondary tile in the Start Screen or the application’s icon in the Application List, a new instance of the application will be launched, and the dormant instance will be lost forever, unless the application supports fast application resume.

Record and display the time at activation

As with the other lifetime events, you need to record the time the event occurred in a property of the App class. Create a new property named ActivatedTime and assign the property value in the Application_Activated method:

public DateTime ActivatedTime { get; private set; }
private void Application_Activated(object sender, ActivatedEventArgs e)
{
    ActivatedTime = DateTime.Now;
}

Add two more TextBlock controls to MainPage.xaml to display the recorded time:

<TextBlock Grid.Row="3" Text="Application activated:" />
<TextBlock x:Name="activated" Grid.Row="3" Grid.Column="1"
    Text="{Binding StringFormat='{0:N0} seconds ago'}" />

Modify the UpdateUserInterface method to assign the DataContext property of the new TextBlock control:

if (app.ActivatedTime != DateTime.MinValue)
    activated.DataContext = (now - app.ActivatedTime).TotalSeconds;

Dormant applications maintain most of their application data and state. When dormant applications are reactivated, there’s usually little work to do. There are situations—for example, when using the sensor APIs—when additional steps are required during activation. We identify these special scenarios in later chapters. You’ll read about one such scenario when working with the camera in chapter 8.

We need to discuss one other reactivation process. In certain situations the operating system will tombstone a dormant application to free up system resources. A tombstoned application is a dormant application that has been terminated, while remaining on the Task Switcher back stack. Application developers need to write special code to save and restore application state during the tombstoning process.

3.3.3. Tombstoning

Windows Phone tombstones an application whenever system resources become scarce. To allow developers the ability to test tombstone recovery code, Microsoft provides an option in the project’s properties to force the operating system to tombstone an application. Figure 3.5 shows the tombstone option.

Figure 3.5. The tombstone debugging option in the Project Properties page

Enable tombstoning in the Lifetime sample project properties and debug the application. Debugging the application is important because the tombstone option doesn’t apply when the application is run outside the debugger. The constructed and launched times should look normal. Press the Start button, wait a few seconds, and press the Back button. The times displayed in the user interface should look weird, as shown in figure 3.6. The construction and activated times look normal, but the launch time isn’t right. Note that the deactivated and navigated from times are still at 0, even though you did navigate away from this page.

Figure 3.6. The Lifetime sample application after being reactivated from a tombstoned state. The weird value for the launched time is due to LaunchedTime defaulting to DateTime.MinValue.

Note that the construction times are 0 seconds ago. The operating system destroyed the instances of the App and MainPage classes that were in memory. When you pressed the Back button, new instances were created. You lost the values that had been stored in the App and MainPage properties. Some of these runtime values don’t need to be saved, but others, like DeactivatedTime, LaunchedTime, and navigatedFromTime, are lost if you don’t store them.

The Windows Phone application framework provides applications with two mechanisms for storing data that should be restored after tombstoning. The process to save and restore the state requires minimum work by the developer, detailed in this section.

Returning tombstoned applications to life

When a dormant application is returned to life, the PhoneApplicationService raises the Activated event. The Activated event is also raised when a tombstoned application is returned to life. The ActivatedEventArgs class provides a boolean IsApplicationInstancePreserved property to allow developers the ability to distinguish between dormant and tombstoned reactivation. You’ll show the value of this property on the user interface alongside the event times. You need to add a new nullable boolean property to the App class to record the IsApplicationInstancePreserved value provided by the event args. Assign a value to the new property in the Application_Activated method:

public bool? IsApplicationInstancePreserved { get; private set; }
private void Application_Activated(object sender, ActivatedEventArgs e)
{
    ActivatedTime = DateTime.Now;
    IsApplicationInstancePreserved = e.IsApplicationInstancePreserved;
}

Modify MainPage.xaml to add controls to display the value:

<TextBlock Grid.Row="4" Text="App Instance Preserved:" />
<TextBlock x:Name="instancePreserved" Grid.Row="4" Grid.Column="1" />

Modify the UpdateUserInterface method in MainPage.xaml.cs to assign the Text property of the instancePreserved TextBlock:

instancePreserved.Text = app.IsApplicationInstancePreserved.ToString();

Debug the application now and you should see a blank in the new control because the nullable boolean property defaults to null. Press the Start button to navigate away and then return to the application using the Back button. You should now see the word False in the instancePreserved TextBlock. If you run the application outside the debugger and perform the same steps, the TextBlock should display the word True.

Method 1: Persisting runtime application data

Now that you know how to detect when the application has been tombstoned, how do you save the DeactivatedTime, LaunchedTime, and navigatedFromTime values? The first facility for persisting application data is the State property exposed on the Phone-ApplicationService. The State property is a dictionary of key-value pairs and will hold any object that can be serialized. Information should be placed into the State dictionary when the application is being deactivated. Modify the Application _Deactivated method to store the DeactivatedTime and LaunchedTime in the State dictionary:

private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
    DeactivatedTime = DateTime.Now;
    PhoneApplicationService.Current.State["DeactivatedTime"]
        = DeactivatedTime;
    PhoneApplicationService.Current.State["LaunchingTime"] = LaunchedTime;
}

Application data stored in the State dictionary can be restored in the Activation event handler. Update the Application_Activated method to restore the Deactivated-Time and LaunchedTime values. The following listing shows the new Application_Activated method.

Listing 3.5. Restoring tombstoned application data

The DeactivatedTime and LaunchedTime values should be restored only when recovering from a tombstoned state. You read from the State dictionaries only when Is-ApplicationInstancePreserved is false . Ask the State dictionary whether it contains a value for the key DeactivatedTime. If the key exists, assign the value in the dictionary to the DeactivatedTime property . Do the same check and assign operation for LaunchedTime .

Method 2: Persisting runtime application data

The second facility for persisting runtime application data is another State dictionary provided by the PhoneApplicationPage. Because PhoneApplicationPage is the base class for MainPage, you can store MainPage’s runtime data separate from the App class’s runtime data. The only MainPage data you need to store is the navigatedFromTime. Update the OnNavigatedFrom method to store the navigatedFromTime right after assigning the variable:

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    if (!e.IsNavigationInitiator))
    {
        navigatedFromTime = DateTime.Now;
        State["NavigatedFromTime"] = navigatedFromTime;
    }
    base.OnNavigatedFrom(e);
}

The sample application writes data to the State dictionary during the OnNavigatedFrom method, but you don’t have to wait until OnNavigatedFrom is called. The State dictionary can be accessed anytime during or after OnNavigatedTo and before or during OnNavigatedFrom. Objects stored in either of the State dictionaries must be serializable.

Restoring runtime application data

The navigatedFromTime is restored in the OnNavigatedTo method. The new and improved OnNavigatedTo method is shown in the following listing.

Listing 3.6. Restoring navigatedFromTime

The only way to determine whether you’re resuming from a tombstoned state is via the IsApplicationInstancePreserved property of the ActivatedEventArgs passed with the Activated event. You stored the IsApplicationInstancePreserved value in a property of the App class, which you use to determine whether the application is resuming from a tombstoned state . If it was tombstoned, and the State dictionary contains the NavigatedFromTime key, then you assign the value stored in the dictionary to the navigatedFromTime field .

Note

In case you’re not familiar with it, C#’s ?? operator, called the null-coalescing operator, is used to assign a default value to a variable. In listing 3.6, if the app.IsApplicationInstancePreserved property is null, the app-Instance-Preserved variable will be assigned the value after the ?? operator, which is true in this example.

Debug the application again, press the Start button, wait a few seconds, and press the Back button. The times displayed in the user interface should look almost normal this time. Note that the launching and navigated from times are farther in the past than the construction times. This is expected because the App and MainPage instances were re-created when the application was activated, but the launching and navigated from times were saved in the State dictionaries.

We’ve demonstrated a couple of procedures for saving a restore application and page state when the operating system reactivates your application. It’s important that the save and restore routines execute reasonably quickly, because the operating system will kill any application that takes longer than 10 seconds to respond to Activated or Deactivated events.

The Launching, Activated, and Deactivated events are all notifications that the application is transitioning into or out of a running state. The last two events we’ll look at, Obscured and Unobscured, are notifications that the application screen is partially or fully hidden, but that the application remains in the running state.

3.4. Out of sight

As you design applications for a phone, you must prepare for situations when the operating system will notify the user about an incoming call or a message. This situation is called obscuration, and in practice this is a partial or full coverage of an application made by a native application. Your application will also be obscured when an alarm or reminder is triggered and, in special circumstances, when the phone’s Lock Screen is activated. In this section we’ll examine how to detect when an application is obscured and then discuss the special circumstances surrounding the Lock Screen.

3.4.1. Obscuration

The event raised when the application gets covered is called Obscured, and the event raised when the application is again fully visible is called Unobscured. Both these events belong to the PhoneApplicationFrame class, and the developer has to explicitly add handlers for these events because they’re not automatically generated by the Visual Studio wizard.

Many applications aren’t affected by obscuration and can ignore the obscuration events. Other applications are more sensitive. An application that plays video may want to pause playback and then resume when the screen is no longer obscured. A game should pause game play and/or game timers so a user isn’t penalized when the screen is inaccessible.

Adding event handlers for obscuration

The App class defines the only PhoneApplicationFrame instance as the static member variable called RootFrame. The following listing shows how to wire up and implement the event handlers in the MainPage class.

Listing 3.7. Adding Obscured and Unobscured event handlers

Two new fields are added to the MainPage class to store the time when the Obscured and Unobscured events are raised. The events are wired up in the MainPage constructor . The App class’s RootFrame property is used to access the Phone-ApplicationFrame instance. In each of the event handlers, the time is assigned to the appropriate field, and the user interface is updated to show the new times .

We leave it as an exercise for the reader to modify UpdateUserInterface so that obscuredTime and unobscuredTime values are displayed in new user interface controls. Hint: You should add four TextBlocks to MainPage.xaml and assign their Text property in the UpdateUserInterface method. Once you’ve added the appropriate code to UpdateUserInterface, launch the application and observe the new behavior. To trigger an obscuration event in the emulator, you can either show the Task Switcher or use the Simulation Dashboard (see figure 3.7) to trigger a reminder or lock the screen. You read about the Simulation Dashboard in chapter 1.

Figure 3.7. The sample application obscured by a reminder. The reminder was triggered using the Simulation Dashboard.

In this section we’ve demonstrated how an application becomes dormant when the user presses the Start button. Another scenario that pauses a running application is when the phone is locked and the Lock Screen is activated. An application isn’t required to go dormant when the phone is locked and may choose to remain running behind the Lock Screen.

3.4.2. Running behind the Lock Screen

All Windows Phones have a Lock button used to lock the phone and power off the device. A phone will also automatically lock after a specified timeout period in which the phone remains idle. The screen timeout duration is specified in the Lock Screen page of the Settings application. The phone is considered idle when the user hasn’t tapped the touch screen.

When the phone is locked, the running application transitions to a dormant state. To see this in action, run the application and lock the screen using the Lock button on a physical device or the Simulation Dashboard with the emulator. When you unlock the screen, you’ll find that the application was deactivated while the screen was locked and then reactivated when the screen was unlocked. Note that the ActivatedTime is reset to 0 seconds ago. This is a feature of the operating system intended to save battery life.

Idle detection mode and obscuration

The PhoneApplicationService provides the ApplicationIdleDetectionMode property to allow applications to continue to run once the phone is locked. When an application wants to run under the Lock Screen, the ApplicationIdleDetectionMode property is assigned the value IdleDetectionMode.Disabled. Once idle detection mode is disabled, it can’t be re-enabled. An attempt to re-enable idle detection will result in an exception.

Note

“App certification requirements for Windows Phone” places certain restrictions on applications that run behind the Lock Screen. Once the screen is locked, the application should no longer attempt to update the user interface and should disable all timers. The battery must be able to power the phone for 120 hours while the application is running under the Lock Screen.

An application is notified when the phone is locked via the Obscured event. The Obscured event handler passes an argument of type ObscuredEventArgs. Obscured-EventArgs contains a boolean property named IsLocked. When IsLocked is true, the application is running behind the Lock Screen.

Letting users choose to run the application behind the Lock Screen

A good application will inform the user that it runs behind the Lock Screen. A better application will allow the user to choose whether the application runs behind the Lock Screen. Modify the Lifetime sample application to provide the user the ability to enable running behind the Lock Screen via a ToggleSwitch control. The Toggle-Switch is one of the controls left out of the Windows Phone SDK but implemented by the Windows Phone Toolkit introduced in the last chapter. The Windows Phone Toolkit is a set of user interface components that mimic controls seen in the native Windows Phone user interface but not provided with the SDK. The toolkit is available for free from CodePlex (http://phone.codeplex.com) and can be added to your project with the NuGet Package Manager. When working with toolkit components in XAML, you need to include an XML namespace declaration in your XAML page:

xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;
 assembly=Microsoft.Phone.Controls.Toolkit"
ToggleSwitch compared to ToggleButton

The ToggleSwitch control is a component used to represent a choice to a user and is similar to the CheckBox and RadioButton controls, which derive from ToggleButton. The ToggleSwitch has a Boolean IsChecked property and raises Click, Check, In-determinate, and Uncheck events. Even though ToggleSwitch has an interface similar to ToggleButton, it doesn’t derive from ToggleButton.

One place where ToggleSwitch differs from ToggleButton is the lack of the IsThreeState property. The ToggleSwitch.IsChecked property can be set to a null value, and the Indeterminate event will be raised, but the user interface will look as if the IsChecked value is false.

Another difference between ToggleButton and Toggle-Switch is how the Content property is used. In a ToggleButton, the Content property is used for label text. In ToggleSwitch, the Content property is used as another way to render the IsChecked property. When the IsChecked property is true, the Content property displays the word On, which you can see in figure 3.8. The word Off is displayed when IsChecked is false or null.

Figure 3.8. The ToggleSwitch control from the Windows Phone Toolkit

If the Content property is used to display On/Off, you might be wondering how you specify label text for a ToggleSwitch. The ToggleSwitch exposes the Header property to add label text to a ToggleSwitch. The switch’s fill color is normally the color specified in the PhoneAccentBrush resource. The label text in the Header is normally rendered using the PhoneBorderBrush theme resource. The PhoneForegroundBrush theme resource is used to render the On/Off text in the Content. The ToggleSwitch provides the SwitchForeground property to allow the developer to specify a new color. The Foreground property controls the color of the On/Off text. Changing the Header text color isn’t so easy.

Adding ToggleSwitch to MainPage.xaml

Now that you know a little bit about the ToggleSwitch, add one to the ContentPanel in MainPage.xaml:

<toolkit:ToggleSwitch x:Name="lockscreen"
   Header="Run while the screen is locked." Grid.Row="10"
   Grid.ColumnSpan="2" Checked="lockscreen_Checked"/>

The ToggleSwitch’s IsChecked property defaults to False. The Checked event is wired up to an event handler called lockscreen_Checked. Inside the event handler, set the ApplicationIdleDetectionMode to Disabled:

void lockscreen_Checked(object sender, RoutedEventArgs e)
{
    PhoneApplicationService.Current.ApplicationIdleDetectionMode
        = IdleDetectionMode.Disabled;
}

Because the ApplicationIdleDetectionMode property can’t be re-enabled, a line of code is added to UpdateUserInterface to disable the ToggleSwitch when idle detection is disabled.

lockscreen.IsEnabled =
   PhoneApplicationService.Current.ApplicationIdleDetectionMode ==
      IdleDetectionMode.Enabled;

To adhere to the certification requirements restricting the use of timers while the phone is locked, the timer must be disabled in the Obscured event handler and re-enabled in the Unobscured event handler, as shown in the following listing.

Listing 3.8. Controlling the timer in adherence to the certification requirements

The IsLocked property of the ObscuredEventArgs is set to true when the Lock Screen is enabled. Because the Obscured event can be triggered by a number of different scenarios, the IsLocked property is examined before stopping the timer . When the unobscured event is triggered, the timer is restarted .

To see how the new code changes the behavior of the Lifetime sample application, run the application and tap the new ToggleSwitch. Lock the phone, wait a few seconds, and unlock the phone. The user interface should show blanks for the Deactivated and Activated event times and show values for Obscured and Unobscured times.

Up to this point in the chapter, you’ve learned how to detect when your application is switched from the foreground to the background. The Lifetime sample application demonstrated the various events raised when an application is launched, deactivated, activated, or closed. We’ve shown you how and when to save application state and quickly recover when your application is switched back to the foreground using the Task Switcher or the Back button.

In the next section you’ll learn how an application can quickly resume in those situations when the user re-launches the application from the Start Screen, Application List, or one of the various extension mechanisms provided by Windows Phone.

3.5. Fast application resume

Fast application switching does a pretty good job of balancing limited operating system resources with application performance. When a user navigates away from an application, the application is placed into a dormant mode and can be quickly reactivated, provided the user follows the necessary steps. But users often do their own thing and expect the application to work. This means a user may try to return to a dormant application using the Start Screen instead of the task switcher and will be frustrated when the application isn’t in the same state it was when they left it.

You can experience this for yourself by launching the Lifetime sample application, pressing the Start button, switching to the application, and then re-launching the application by tapping its icon in the list. You should notice that all the application and page times have been reset to 0.

Enabling fast application resume

Microsoft’s solution to this frustrating user experience is fast application resume. When a dormant application supports fast application resume, the operating system will reactivate it instead of launching a brand-new instance. Applications generated by the Visual Studio project templates don’t support fast application resume by default. Enabling fast application resume is simple and consists of setting the Activation-Policy attribute of the DefaultTask element in the WMAppManifest.xml file. The file must be opened with the XML editor because the manifest editor doesn’t provide access to the activation policy. To open the file with the XML editor, right-click the file in the Solution Explorer and select Open With from the context menu. Then select XML (Text) Editor from the list. Once you have the file open, add the ActivationPolicy attribute and set its value to Resume:

<Tasks>
   <DefaultTask Name="_default" NavigationPage="MainPage.xaml"
      ActivationPolicy="Resume" />
</Tasks>

After making this change in the Lifetime sample application, execute the previous workflow to launch, switch away, and re-launch the application. This time you should notice that application and pages times were not reset to 0. With fast application switching, the Launched, Activated, Deactivated, and Closing lifetime events are used by an application to properly handle switching scenarios. With fast application resume, an application uses navigation modes to properly handle resume scenarios.

3.5.1. Navigation modes

In chapter 2 you learned that Windows Phone applications are XAML navigation applications. Behind the scenes, the application host calls the Navigation Service directly to navigate to MainPage.xaml during initial launch, and when an application is reactivated, the application host navigates directly to the active page’s XAML. Pages and other XAML controls detect navigation events through their OnNavigatedTo, OnNavigating-From, and OnNavigatedFrom methods. The event argument types sent to each of these methods (NavigationEventArgs and NavigatingCancelEventArgs) contain a property named NavigationMode of type System.Windows.Navigation .Navigation-Mode. The possible values of the NavigationMode property are listed in table 3.2.

Table 3.2. NavigationMode values

Navigation mode

Description

Back Navigation initiated with the Back button or the Navigation Service’s GoBack method. Also used when returning to a dormant application via the Back button or the Task Switcher.
Forward Navigation initiated with the Navigation Service’s GoForward method.
New Navigation initiated with the Navigation Service’s Navigate method. Also used when the application is first launched or when a dormant application is re-launched to a new page or URI.
Refresh Used when re-launching a dormant application to an already active page or URI.
Reset Used when re-launching a dormant application via the Start Screen, Application List, or other extension mechanism.

When an application supports fast application resume, re-launching an application results in a two-step navigation process. First, the application receives a set of navigation events with the NavigationMode value of Reset. This informs the application that it’s being restarted from a dormant state. The second step of the process depends on the currently active page and the URI used to launch the application, as shown in figure 3.9.

Figure 3.9. The default fast application workflow. If the launch URI matches the active page’s URI, the active page is refreshed; otherwise a new page is created.

When the launch URI matches the URI of the active page, the application raises a set of navigation events with a NavigationMode of Refresh. This is the scenario in the Lifetime sample application. MainPage.xaml is the only page, and when launched from the application list, the launch URI is /MainPage.xaml. To examine the behavior when the launch URI doesn’t match the active page, the Lifetime application needs to support alternative launch URIs.

3.5.2. Resuming with an alternative URI

The Hello World sample application in chapter 2 demonstrates how to pin a secondary tile to the Start Screen, which specified an alternative launch URI. The Lifetime application will implement a similar approach to launch MainPage.xaml with an alternative URI.

Open MainPage.xaml and add the following markup to create an ApplicationBar with a single button (or use the visual editor):

<phone:PhoneApplicationPage.ApplicationBar>
   <shell:ApplicationBar IsMenuEnabled="False" Mode="Minimized">
      <shell:ApplicationBarIconButton IconUri="/Assets/AppBar/favs.png"
         IsEnabled="True" Text="pin" Click="pin_Click"/>
   </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Inside the button’s click event handler, shown in the following listing, a new secondary tile will be created with a unique launch URI.

Listing 3.9. Pinning a secondary tile to the Start Screen

You start by reading the count of active tiles from the ShellTile ActiveTiles collection. This number is used to create a new URI to MainPage.xml with a query string parameter named context.

After adding the button and the button’s click event handler, start the sample application and tap the Pin button to add a secondary tile to the Start Screen. When the new tile is created, the application is placed in a dormant state, and the operating system navigates to the Start Screen, showing the newly pinned secondary tile. Tap the new secondary tile to re-launch the application. You should see the main page with page constructed time reset to 0 because a new instance of MainPage was created and existing instances of MainPage were cleared from the navigation stack. If you press the Start button and then press the secondary tile again, the application will re-launch, but this time the page constructed times shouldn’t be reset because the URI matched the active page.

Up to this point, we haven’t added any code to the sample application to support fast application resume. Between the operating system and code generated by the Windows Phone App project template, the application automatically supports a default resume workflow. Every application is different, and the resume workflow might need to be customized to provide a better user experience.

3.5.3. Customizing the resume experience

The default resume workflow provided by the operating system and generated code always navigates to the launch URI. This default workflow doesn’t fix the frustrating user experience mentioned when we introduced fast application resume a few pages back. When returning to a dormant application using the primary tile or the icon in the Application List, a user expects the application to be in the same state it was in when they left it. If your application supports multiple launch URIs or internally navigates to multiple pages, the primary tile will cause your application to discard current state and re-launch the main page.

Canceling default navigation

In this section you’ll update the Lifetime application to cancel the default navigation when the user resumes the application using the primary tile. The new workflow is shown in figure 3.10.

Figure 3.10. The customized fast application resume workflow for the Lifetime sample application. When the URI of the active page isn’t /MainPage.xaml, and the launch URI is /MainPage.xaml, the navigation will be canceled.

When you created the sample application with the Windows Phone App project template, the App.xaml.cs file was generated with code to support the default fast application resume scenario. This support is implemented in the CheckForReset-Navigation method, an event handler for the root frame’s Navigated event. When the root frame receives a navigation event with a navigation mode value of Reset (when a dormant application is re-launched), the CheckForResetNavigation method wires up a new Navigated event handler to clear the navigation back stack.

To implement the customized workflow, the CheckForResetNavigation method in App.xaml.cs must wire up a new Navigating event handler, named Cancel-Navigation-AfterReset, when the launch URI is /MainPage.xaml:

void CheckForResetNavigation(object sender, NavigationEventArgs e)
{
   if (e.NavigationMode == NavigationMode.Reset)
   {
       RootFrame.Navigated += ClearBackStackAfterReset;
       if (e.Uri.ToString() != "/MainPage.xaml")
          RootFrame.Navigating += CancelNavigationAfterReset;
   }
}

When a Navigating event is raised, the event handler can cancel the navigation by setting the event args’ Cancel property to true. The CancelNavigationAfterReset event handler will set the Cancel property to true when the navigation mode is New and the target URI is equal to /MainPage.xaml:

void CancelNavigationAfterReset(object sender,
   NavigatingCancelEventArgs e)
{

   RootFrame.Navigating -= CancelNavigationAfterReset;
   if (e.NavigationMode == NavigationMode.New
      && e.Uri.ToString() == "/MainPage.xaml")
   {
      e.Cancel = true;
   }
}

After updating CheckForResetNavigation and adding the implementation of the CancelNavigationAfterReset method, start the Lifetime application. Once it’s running, press the Start button and tap the secondary tile to re-launch the application with a new URI. When the application restarts, confirm that the page constructed times are reset to 0. Press the Start button once again and make sure to use the primary tile or Application List icon to re-launch the application. This time the page constructed times shouldn’t be reset, even though the application was launched with a URI different from that of the active page.

When designing the workflow for your own application, you should consider whether the application should support fast application resume and, if so, how the re-launch workflow should be customized. For each page in your application, consider what the user should experience when they leave the application and return to it from tiles on the Start Screen, Application List, or other extension mechanisms.

3.6. Summary

Throughout this chapter we’ve covered features of the Windows Phone that allow applications to appear to be continuously running when they’re not. A continuously running application can pose a problem for a mobile device with limited resources. A running application might interfere with the power-management routines of the operating system and rapidly drain the battery. Runaway background applications steal processing power from foreground applications, resulting in a slow and unresponsive user experience.

Microsoft has designed the Windows Phone to provide the best user experience possible. The Windows Phone imposes limitations to ensure that applications can’t intentionally or accidentally create performance or power problems for the user. One of these limitations prevents an application from running when it’s not the foreground application.

You learned how to detect when your application is switched from the foreground to the background. The Lifetime sample application demonstrated the various events raised when an application is launched, deactivated, activated, closed, and resumed. We showed how and when to save application state and quickly recover when your application is switched back to the foreground.

You now know that a dormant background application can’t perform any work. How can you create killer applications if they can’t do any work in the background? The answer lies with the Scheduled Action Service. In the next chapter you’ll create a new sample application to explore alarms, reminders, and background tasks—three different kinds of scheduled actions. Scheduled actions allow your application to alert the user or execute background work when your application isn’t running.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.27.58