Application Life-cycle Management in Windows 8

  • 4/15/2013

Resuming

In the “Suspending” section of this chapter, you implemented a suspension event handler in the application class to calculate and save the current suspension time to the application data storage.

In this procedure, you will read the saved time from the application data storage during the resume operation from the application class, and then you will implement the code to show the same data within a page.

The resume operation is useless if the application was suspended by the system because the memory dedicated to the application is just frozen and not cleared. Instead, if the system needed more memory and decided to terminate the application, the resume operation is the right place to read the data saved in the suspension procedure.

You can intercept the resume operation hooking up the Resuming event of the application class if you want to perform some operations on the application. For instance, you can save the page the user had open before the suspension and, in case of application termination, open that page instead of the default one, as you can see in the following code sample:

static Platform::String^ currentPage;

App::App()
{
        InitializeComponent();
        Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending);
        Resuming += ref new EventHandler<Platform::Object^>(this, &App::OnResuming);
}

void App::OnSuspending(Object^ sender, SuspendingEventArgs^ e)
{
    (void) sender;        // Unused parameter

    auto def = e->SuspendingOperation->GetDeferral();

    auto settingsValues = Windows::Storage::ApplicationData::Current->LocalSettings->Values;
    if (settingsValues->HasKey("Page"))
    {
            settingsValues->Remove("Page");
    }
    settingsValues->Insert("Page", currentPage);

    def->Complete();

}

void App::OnResuming(Object^ sender, Platform::Object^ e)
{
    (void) sender;        // Unused parameter
    auto settingsValues = Windows::Storage::ApplicationData::Current->LocalSettings->Values;
    if (settingsValues->HasKey("Page"))
    {
        if (dynamic_cast<Platform::String^>(settingsValues->Lookup("Page")) ==
            "CustomerDetails")
        {
            // Activate the Customer Details Page
        }
    }

}

The code is straightforward: the OnSuspending event handler saves the name of the current page in the local application data store, and the OnResuming event handler reads that value when the application is resumed from a terminated state.

An application can leverage the resuming operation, performing some actions even if the application was not terminated. For instance, you can request data taken from a web service or remote source if the suspend operation was done some minutes before the resume, in order to present fresh content to the user.

Refresh data during resume

In this procedure, you will modify the code for the MainPage.xaml.cpp file to display the time the page was launched, suspended, and resumed.

  1. Open the MainPage.xaml file and add three TextBlocks. The first one will display the time the page was first opened, the second will display the time the page was suspended, and the third will display the time the page was resumed. Use this code as a reference:

    <Page
        x:Class="ALMEvents.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:ALMEvents"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <StackPanel Orientation="Vertical" VerticalAlignment="Top" Margin="10,01,10,10">
                <Button Click="Close_Click" Content="Close" />
                <TextBlock Name="firstTime" FontSize="24" Margin="10,10,10,10" />
                <TextBlock Name="suspendTime" FontSize="24" Margin="10,10,10,10" />
                <TextBlock Name="resumeTime" FontSize="24" Margin="10,10,10,10" />
            </StackPanel>
        </Grid>
    </Page>
  2. Open the MainPage.xaml.h file and add check that the code corresponds to the following listing:

    #pragma once
    #include "MainPage.g.h"
    namespace ALMEvents
    {
            /// <summary>
            /// An empty page that can be used on its own or navigated to within a Frame.
            /// </summary>
            public ref class MainPage sealed
            {
            public:
                    MainPage();
    
            protected:
                    virtual void OnNavigatedTo(
                        Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
            private:
                    void Close_Click(Platform::Object^ sender,
                        Windows::UI::Xaml::RoutedEventArgs^ e);
                    void Current_Resuming(
                        Platform::Object^ sender, Platform::Object^ e);
            };
    }
  3. Open the MainPage.xaml.cpp file and use the following code as a reference to hook up the resuming event to display the suspended time restored from the application state and the resumed time.

    #include "pch.h"
    #include "MainPage.xaml.h"
    
    using namespace ALMEvents;
    
    using namespace Platform;
    using namespace Windows::Foundation;
    using namespace Windows::Foundation::Collections;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Controls::Primitives;
    using namespace Windows::UI::Xaml::Data;
    using namespace Windows::UI::Xaml::Input;
    using namespace Windows::UI::Xaml::Media;
    using namespace Windows::UI::Xaml::Navigation;
    using namespace Windows::Globalization::DateTimeFormatting;
    
    // The Blank Page item template is documented at
    // http://go.microsoft.com/fwlink/?LinkId=234238
    
    MainPage::MainPage()
    {
        InitializeComponent();
        Windows::Globalization::Calendar^ now = ref new Windows::Globalization::Calendar();
        now->SetToNow();
        firstTime->Text = "Ctor : " + DateTimeFormatter::LongTime::get()->
           Format(now->GetDateTime());
        App::Current->Resuming += ref new EventHandler<Platform::Object^>(this,
           &MainPage::Current_Resuming);
    }
    
    void MainPage::Current_Resuming(Platform::Object^ sender, Platform::Object^ e)
    {
        this->Dispatcher->RunAsync(
            Windows::UI::Core::CoreDispatcherPriority::Normal,
                ref new Windows::UI::Core::DispatchedHandler(
                    [this]() {
                auto settingsValues = Windows::Storage::ApplicationData::Current->
                   LocalSettings->Values;
                if (settingsValues->HasKey("SuspendedTime"))
                {
                    suspendTime->Text = "Suspended : " + settingsValues->
                       Lookup("SuspendedTime")->ToString();
                }
                Windows::Globalization::Calendar^ now =
                   ref new Windows::Globalization::Calendar();
                now->SetToNow();
                resumeTime->Text = "Resumed :" + DateTimeFormatter::LongTime::get()->
                    Format(now->GetDateTime());
            }));
    }
    
    void ALMEvents::MainPage::Close_Click(Platform::Object^ sender,
         Windows::UI::Xaml::RoutedEventArgs^ e)
    {
            Application::Current->Exit();
    }
    
    /// <summary>
    /// Invoked when this page is about to be displayed in a Frame.
    /// </summary>
    /// <param name="e">Event data that describes how this page was reached. The Parameter
    /// property is typically used to configure the page.</param>
    void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
    {
            (void) e;        // Unused parameter
    }

    In the MainPage class constructor, the second and subsequent lines of code assign the current time to the first label. This code is executed only when the application instantiates the page, which occurs when the user launches the application or when the application is resumed from a terminated state. This code is not executed when the application is resumed from the suspended state.

    The Current_Resuming event handler reads the value of the SuspendedTime key in the application data store and assigns it to the second label. It then assigns the current time to the last label. This code is not executed in the UI thread, which is why the code is executed by a dispatcher.

  4. Deploy the application.

  5. Launch the application from the application tile on the Start screen and close the initial dialog box.

  6. Press the Windows key and then go to the desktop.

  7. Open Task Manager and wait until the application is suspended by the system.

  8. Minimize Task Manager.

  9. Return to the application by pressing Alt+Tab. The result is shown in the following screen shot.

    httpatomoreillycomsourcemspimages1615036.jpg
  10. Close the application using the Close button.

To facilitate debugging the suspending and resuming events, Visual Studio provides two menu items that enable you to ask WinRT to suspend and resume the application during a debugging session. This feature is useful because it lets you avoid using Task Manager, and you can invoke this event as needed.

Use Visual Studio to debug the suspending and resuming events

In this procedure, you will use Visual Studio to debug the suspending and resuming events.

  1. Open the App.xaml.cpp file and place a breakpoint in the first line of the OnSuspending method.

  2. Open the MainPage.xaml.cpp file and place a breakpoint in the first line of the Current_Resuming method.

  3. Press F5 to start a debugging session and wait until the application is visible on the screen.

  4. Press Alt+F4 to return to Visual Studio, and in the Debug Location toolbar choose Suspend. If this toolbar is not visible, you can enable it by choosing the Toolbars item from the View menu, and then selecting the Debug Location item. The toolbar is visible in the following graphic.

    The breakpoint in the suspend event handler will be hit. Press F5 to continue. The breakpoint in the resume event handler will be hit soon because the application was taken to the foreground by Visual Studio when you pressed F5.

  5. Press F5 again and verify that the application is visible and presents the three labels with different times.

  6. Press Alt+F4 to return to Visual Studio, and use the Debug Location toolbar to select Resume to verify you can debug directly the resume procedure without the need to debug the suspend procedure first. You can also click the Resume button on the Debug Location toolbar.

  7. Using the Debug Location toolbar, select Suspend and Shutdown. The application will first go in the suspended state and then will be terminated by the runtime. With this option, you can debug the code for the OnLaunched event to test a previous termination.

To summarize, the system suspends your app whenever the user switches to another app or to the desktop, and the system resumes your app whenever the user switches back to it. When the system resumes your app, the content of your variables and data structures is the same as it was before the system suspended the app—in other words, the system restores the app exactly where it left off, so that it appears to the user as if it’s been running in the background the whole time. However, the app may have been suspended for a significant amount of time, so it should refresh any displayed content that might have changed while the app was suspended, such as news feeds or the user’s location.