Поделиться через


Part 2: Manage life cycle and state (Windows Runtime apps using C++)

[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]

In this part of the C++ tutorial series, you update your "Hello, world" app to respond to life-cycle events, and save user and session data.

You can launch several Store apps and switch between them without worrying about slowing down the system or running down the battery. That's because the system automatically suspends—and sometimes closes—apps that are running in the background. A well-designed app can be suspended, closed, and restarted by the system and still seem as though it was running the entire time.

Before you start...

  • This is the second tutorial in a series. Before you start this tutorial, read Hello World in C++.
  • The complete code for this tutorial is in the Hello World (C++) sample on Code Gallery.

About the app's life cycle

Before you go back to the code, let's talk a bit about the app's life cycle. At any given point, an app is either not running, running, or suspended. The transition from not running to running is called activation; it marks the beginning of the app's life cycle.

When an app is running, it can be suspended when the user switches away from it or the operating system enters a low power state. While your app is suspended, it continues to reside in memory so that the user can quickly switch back to it. No extra code is required to handle the transition between suspended and resuming.

But Windows can also close a suspended app at any time to free up memory or to save power. When your app is closed, it's unloaded from memory and goes into a not running state. When the user starts it again, the system must activate it, and that might mean that the app data and user data must be reloaded.

When the user closes an app by pressing Alt+F4 or by using the close gesture, the app is suspended for 10 seconds and then is closed.

Windows notifies your app before it suspends it, but not before it closes it. You handle the OnSuspended event to save state and release resources and file handles. When the app is activated, you handle the OnLaunched event to restore state and make it seem to the user that the app never stopped running.

Step 1: Using SuspensionManager

In Hello World in C++, the default file MainPage.xaml is used. It's based on the Blank Page template, which has no support for navigation or the app life-cycle. Now, we're going to replace MainPage with a Basic Page. When you add a Basic Page to a project for the first time, Visual Studio also adds several files to the project in the \Common\ folder. One of these files contains the NavigationHelper class, which handles the details of forward and backward navigation through your app's pages. The other class that's added is SuspensionManager, which helps serialize the page-state data in your app's local storage when the app is suspended, and restore it when the app is activated. In this tutorial, we'll focus on Suspension Manager.

Perform the following steps in both the Windows Phone 8.1 project and the Windows 8.1 project:

To add a basic page to the project

  1. Right-click MainPage.xaml, click Remove, and then choose the option to permanently delete the file. (This also deletes the two code-behind pages.)

  2. On the menu bar, click Project > Add New Item, and then in the left pane of the dialog box, select XAML, and in the center pane, select Basic Page. Name this page MainPage and then click Add. A BasicPage has a lot more markup than a BlankPage. Its grid panel has two rows. In the Windows project, the top row contains a navigation button and a TextBlock that shows the app's title—by default, "My Application"—which is defined in the <x:String x:Key="AppName">My Application</x:String> element in the Page.Resources node in MainPage.xaml. In the Windows Phone 8.1 project, the top row contains two text blocks, one for the page title and one for the app title. There is no back button because phone apps use the hardware back button.

  3. Let's change the app title to "Hello World." Although we could change it in the Resources element in MainPage.xaml, a better place to define the resource is in App.xaml in the shared project, because both the Windows 8.1 project and the Windows Phone 8.1 project can use it. First, delete the resource in MainPage.xaml, then in App.xaml in the HelloWorld.Shared project, just before the closing tag, insert this <Application.Resources> node:

        <Application.Resources>
            <x:String x:Key="AppName">Hello World</x:String>
        </Application.Resources>
    

    The pageTitleTextBlock binds to the static resource AppName, like this: <TextBlock x:Name="pageTitle" Text="{StaticResource AppName}". It now finds that resource in the App class because we deleted the local override in the page class.

  4. Next, we paste in the XAML that we added in the previous tutorial, except that here it's modified a bit—the text block that held the app title is eliminated because the title is now displayed in the title grid. And in the StackPanel we added a Grid.Row attribute to correctly place the StackPanel on the new page.

    Windows 8.1: We put our XAML in the bottom row of the root grid so that it shows up beneath the grid that contains the navigation button and title.

            </Grid>
              <!-- PASTE HERE -->
        </Grid>
    </Page>
    
    <StackPanel Margin="24,20,20,20">
        <TextBlock Style="{ThemeResource BaseTextBlockStyle}" FontSize="16" Text="What's your name?"/>
        <TextBox x:Name="nameInput" Width="300" HorizontalAlignment="Left"/>
        <Button Content="Say &quot;Hello&quot;"/>
        <TextBlock Style="{ThemeResource BaseTextBlockStyle}" FontSize="16" x:Name="greetingOutput"/>
    </StackPanel>
    

    Windows Phone 8.1: A basic page is somewhat different in the Phone project. Paste the following XAML in the contentRoot grid element. Because the phone screen is typically narrower than a tablet or PC screen, we stack the controls vertically. (Vertical is the default orientation.)

            <Grid Grid.Row="1" x:Name="ContentRoot">
                <!-- PASTE HERE -->
            </Grid>
    
    <StackPanel Margin="24,20,20,20">
        <TextBlock Style="{ThemeResource BaseTextBlockStyle}" FontSize="16" Text="What's your name?"/>
        <TextBox x:Name="nameInput" Width="300" HorizontalAlignment="Left"/>
        <Button Content="Say &quot;Hello&quot;"/>
        <TextBlock Style="{ThemeResource BaseTextBlockStyle}" FontSize="16" x:Name="greetingOutput"/>
    </StackPanel>
    
  5. In the TitlePanel, point the app title TextBlock to the global AppName that we added to App.xaml as a static resource—just like in the Windows project—and delete the second TextBlock because we don't need a page title. The title panel now looks like this:

    <!-- TitlePanel -->
    <StackPanel Grid.Row="0" Margin="24,17,0,28">
        <TextBlock Text="{StaticResource AppName}" Style="{ThemeResource TitleTextBlockStyle}" Typography.Capitals="SmallCaps"/>
    </StackPanel>
    
  6. Let's not forget to add the event handler! In the first tutorial, we used the Properties window (F4) to add an event handler. Here's another way: in the Button element just after the content attribute, start typing Click; a list should appear and "Click" should be pre-selected, depending on how much you have typed.

    Press Tab to accept the suggestion. A box that says "New event handler" should appear. Press Enter to have Visual Studio create the function prototype in the .h file and an implementation stub in the .cpp file. With this approach, you have to accept the default name, which will be something like Button_Click. (To change the name, you can press Ctrl+Shift+H to replace it project-wide.) Press F12 to Go To Definition and add this code to the implementation:

    greetingOutput->Text = "Hello, " + nameInput->Text + "!";
    

Next, lets' register the main app Frame so that SuspensionManager can save and restore the navigation state for each page that the Frame holds. You register the Frame immediately after it's created in the OnLaunched method in App.xaml.cpp.

To save the Frame state

  1. In Solution Explorer, in the shared project at the bottom, open App.xaml.cpp.

  2. Add the following #include directive.

    #include "Common\SuspensionManager.h"
    
  3. In the OnLaunched method, call the SuspensionManager::RegisterFrame static method to register the root Frame. Place this statement directly after the rootFrame = ref new Frame(); statement.

    HelloWorld::Common::SuspensionManager::RegisterFrame(rootFrame, "appFrame");
    
  4. Repeat steps 1 through 3 for the Windows Phone 8.1 project.

    Note  Use the context chooser drop-down at the top of the code editor to match the project you are currently working in. By selecting the correct context, you ensure that the code editor will gray-out code that isn't compiled for the project. When you select the Windows project in the context chooser...

    ...this is how App.xaml.h appears:

     

That's all we have to do to save the frame state. The SuspensionManager class does the rest. You can view that class definition more closely in the Common folder in your project.

Step 2: Save the page state

Generally, there are two kinds of data to manage in an app: app data and session data. App data persists across sessions and must always be accessible to the user. In your app, the Text of the nameInputTextBox is app data. Always save important app data incrementally whenever the app is running. Because your app has only five or fewer seconds to run code in the suspending event handler, you have to ensure that important app data is completely saved to persistent storage before the app is suspended. Don't hold megabytes worth of app data and then try to save it all at once when the app is suspended.

The Windows.Storage.ApplicationData class helps manage app data. This class has a RoamingSettings property that returns an ApplicationDataContainer that can store app data that persists across sessions. Let's store the user's name in the roaming ApplicationDataContainer as the user types it in.

Note  The RoamingSettings app data container makes it easy to store data in a way that's accessible to the user across multiple machines. Basically, the data is uploaded to the cloud in the background. You can also use the LocalSettings app data container, but only when you want to store machine-specific info.

 

Save persistent app data as often as it makes sense in your app. Here, you handle the TextBox::TextChanged event on the nameInputTextBox and save the user's name as the user enters it.

To save app data

  1. In the nameInput XAML tag, start typing "TextChanged," then press Tab. When the event-handler box appears, press Enter. Press F12 to go to the event handler that was created—nameInput_TextChanged and then add this code to save the nameInput text in roamingSettings:

     auto roamingSettings =
            Windows::Storage::ApplicationData::Current->RoamingSettings;
        roamingSettings->Values->Insert("userName", nameInput->Text);
        if (roamingSettings->Values->HasKey("userName"))
        {
            nameInput->Text = roamingSettings->Values->Lookup("userName")->ToString();
        }
    
  2. Press F5 to build and run the app. Your name is saved as you enter it in the text box. Later, this tutorial shows how to verify that.

  3. Repeat steps 1-2 for the Windows Phone 8.1 project.

Session data is temporary data that's relevant to the user’s current session in your app. A session ends when the user closes the app, or logs off the device or reboots it. You restore it only if the operating system suspends and closes the app. (Remember, just suspending an app doesn't remove it from memory, and so no state is lost in that case.) You use the SuspensionManager class to save session state in the Application::Suspending event handler in App.xaml.cpp. Save your state here because the operating system might decide to close the app later and there is no notification for that event. Our session data consists of:

  • The navigation state of the app Frame, so that the app can be restored to the same page is was on.

  • The state of each page. In our case, we have one page that has one state variable: the greetingOutput text.

To save the session state

  1. In Solution Explorer in the shared project, open App.xaml.cpp.

  2. Add this using directive at the top of the file:

    using namespace concurrency;
    
  3. In the OnSuspending event handler method, add these lines:

     auto deferral = e -> SuspendingOperation->GetDeferral();
     create_task(HelloWorld::Common::SuspensionManager::SaveAsync())
     .then([deferral]() {deferral->Complete(); });
    

    Calling SaveAsync saves the navigation state of the Frame and then gives your Page the opportunity to save its content in its SaveState method. We'll talk more about the create_async and then methods in a later tutorial.

  4. To save the page state, in Solution Explorer, open MainPage.xaml.cpp and then add this code to the SaveState method:

    // Insert adds a new key or replaces value if key already exists.
    e->PageState->Insert("greetingOutputText", greetingOutput->Text);
    

    The SuspensionManager class serializes and saves the pageState dictionary to an XML file. Data saved in pageState is saved only for the current session.

  5. Repeat step 4 for the Windows Phone 8.1 project. (Steps 1-3 apply to both projects.)

  6. Choose Build > Build solution to make sure the app builds with no errors. We'll test the functionality after we add support for app state.

Step 3: Restoring the app's state in the OnLaunched event handler

Earlier, you saw that the App.xaml.cpp file contains code that handles your app's activation. There are many different ways to activate an app. Here we look at the Launch activation and the OnLaunched method.

An app is launched whenever it wasn't running and the user starts it. When an app is launched in this way, Windows displays the app's splash screen.

In App.xaml.cpp, the code that handles activation defines an override of the OnLaunched method. This code is executed only if the activation is a Launch activation. (You can override other methods to handle other kinds of activation, but we don't do it here. For more info, see Application lifecycle.)

Basically, the code performs these steps:

  • Declares a Frame and tries to assign the current window contents to it.

  • If the window already contains a Frame, the app is already initialized in memory and so Frame initialization is skipped.

  • If the app isn't initialized, the code creates a Frame to host the app pages. You register the Frame with the SuspensionManager. This is the code you added in Step 1.

  • If the window already contains a Frame, the app is already initialized in memory and so Frame initialization is skipped.

  • Next, the code checks the previous execution state to see how the app was last closed. When the previous execution state is Terminated, it means that the last time the app ran, Windows successfully suspended it and then closed it. In that case, you have to restore the app's state.

  • Next, the code checks whether the Frame contains any content. If the app is running, or the navigation state was restored, the Frame already has content. Otherwise, the Frame navigates to the first page in the app. In this case, it navigates to MainPage.

  • Finally, the code activates the window.

Now that you know what happens when the app is started, let's look at how to restore the app state.

To restore the app's state

  1. If the app was closed, call the SuspensionManager::RestoreAsync method.

    Calling RestoreAsync restores the navigation state of the Frame and then calls LoadState on each Page in the Frame.

    if (e->PreviousExecutionState == ApplicationExecutionState::Terminated)
    {
                        HelloWorld::Common::SuspensionManager::RestoreAsync();
    }
    
  2. In MainPage.xaml.cpp in the Windows 8.1 project, add code to the LoadState method to restore the page state.

    First, check to see whether the pageState dictionary exists and has a key named greetingOutputText. If the key exists, use it to restore the greetingOutput text.

    Next, load the user name. Because you want the user-name data to persist over multiple sessions, you store it in the RoamingSettings app data container. Let's add some code to see whether the user name exists and, if it does, to display it.

    Here's the complete code for the LoadState method.

    void MainPage::LoadState(Object^ sender, Common::LoadStateEventArgs^ e)
    { 
        (void) sender;  // Unused parameter
           // Restore values stored in session state.
           if( e->PageState != nullptr && e->PageState->HasKey("greetingOutputText"))
           {
                greetingOutput->Text = e->PageState->Lookup("greetingOutputText")->ToString();
           }
    
           // Restore values stored in app data.
           auto roamingSettings =
                  Windows::Storage::ApplicationData::Current->RoamingSettings;
           if (roamingSettings->Values->HasKey("userName"))
           {
               nameInput->Text = roamingSettings->Values->Lookup("userName")->ToString();
           }
    }
    
  3. Repeat steps 1 and 2 for the Windows Phone 8.1 project.

Now you can build and run the app, and examine how the session state is saved and restored. To test a project, right-click the node in Solution Explorer and then click Set as Startup Project. The project node is now bold in Solution Explorer. So far, you've tested your app in Visual Studio by running it in debug mode, and stopped it by selecting Debug > Stop Debugging. But doing this causes the app to perform a normal shutdown, and the Suspending event doesn't occur. Fortunately, you can use other functionality in Visual Studio to simulate suspending, closing, and restoring an app.

To simulate suspending, closing, and restoring an app

  1. Set the desired project as the startup project, then press F5 to run the app in debug mode.

  2. Enter your name in the input box and click "Say "Hello"". The greeting is displayed.

  3. In Visual Studio, open the Lifecycle Events drop-down menu on the Debug Location toolbar.

    By default, the Debug Location toolbar appears when the debugger is running. If it's not there, click View > Toolbars > Debug Location to show it.

  4. Select Suspend and shutdown.

    Visual Studio simulates suspending and closing your app, and so the Suspending event occurs and your state management code is executed.

  5. Press F5 to run the app again. The app is restored to its previous state.

  6. Type a different name in the text box, and choose "Say "Hello"".

  7. In Visual Studio, close the app by selecting Debug > Stop Debugging.

  8. Press F5 to run the app again.

    Notice that the name that you last typed is restored because it was saved as you typed it in. The greeting is not restored because the Suspending event didn't occur, and so the session state wasn't saved.

  9. Set the other project as the startup project and try the same thing.

Summary

Congratulations, you're done with the second tutorial! It taught how to save app data and session state in a Store app.

See the code

Did you get stuck, or do you want to check your work? If so, see the Hello World (C++) sample on Code Gallery.

Next steps

The next part of this tutorial series teaches how to use the design tools in Visual Studio to create a more complex UI. See Navigation, layout, and views (C++).