Condividi tramite


Part 2: Manage app lifecycle and state (HTML)

[ 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 ]

You can launch several apps and switch between them without having to worry about slowing down the system or running the battery down. That's because the system automatically suspends (and sometimes terminates) apps that are running in the background. By saving an app's state when it's suspended or terminated and restoring the state when it's relaunched, you can make it seem as though your app never stopped running.

Learn how to:

  • Save state using different types of roaming storage
  • Restore your app's state the next time the app is launched

Tip  

If you want to skip the tutorial and go straight to the code, see Getting started with JavaScript: Complete code for the tutorial series.

 

Before you start...

About the app's lifecycle

In the previous tutorial, we described how the default.js file contains code that handles our app's activation. Before we go back to the code, let's talk a bit about our app's lifecycle.

At any given point, an app is not running, running, or suspended.

An app can be suspended when the user switches away from it or when the device enters a low power state. While your app is suspended, it continues to reside in memory so that users can quickly and reliably switch between suspended apps, resuming them. When your app is suspended and then resumed, you don't have to write any extra code to make it look as though it had been running the entire time.

The operating system can also terminate a suspended app at any time to free up memory for other apps or to save power. When your app is terminated, it stops running and is unloaded from memory.

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

The operating system notifies your app when it suspends it, but doesn't provide additional notification when it terminates the app. That means your app must handle the suspended event and use it to save its state and release its exclusive resources and file handles immediately.

To create a good user experience, we want our app to look like it never stopped running. That means the app needs to retain any data the user entered, settings they changed, and so on. That means we need to save our app's state when we suspend the app, in case the operating system terminates it, so that we can restore its state later.

There are two types of data for you to manage in your app: app data and session data.

In the next steps, we learn how to update our app to save these types of data. What state do we need to save? Right now, the only thing the user can change is their name entry. The user can also click the Say "Hello" button to generate a personalized greeting.

Step 1: Save app data

App data persists across sessions and must always be accessible to the user. In our app, the value of the nameInputinput box is app data—a setting that should appear in the app whenever the user runs it. Because your app has only up to five seconds to run code in the suspending event handler, you need to ensure that your important app data is saved to persistent storage by the time the app is suspended (or terminated). The best way to do this is to save app data incrementally when it changes.

We can use the Windows.Storage.ApplicationData object to help us manage our app data. This object has a roamingSettings property that returns an ApplicationDataContainer. We can use this roaming ApplicationDataContainer to store user data that persists across sessions. Let's store the user's name and the rating in the roaming ApplicationDataContainer as the user types it in.

Note  This tutorial shows how to use roamingSettings. The roaming settings app data container makes it easy to store data in a way that is accessible to the user across multiple machines. Basically, the data is uploaded to the cloud in the background for you. You can also use the local settings app data container (localSettings), but you should only use it when you want to store machine-specific info.

 

We start here with the code from Part 1: Create a "Hello, world!" app.

To save app data

  1. In your default.js file, create an event handler for the nameInputinput box's change event. Name the event handler nameInputChanged. Add this code just after the buttonClickHandler we created in Part 1.

        function nameInputChanged(eventInfo) {
    
        }
    
  2. Use the eventInfo object's srcElement property to access the nameInput control. (The srcElement property retrieves the element that fired the event.)

        function nameInputChanged(eventInfo) {
            var nameInput = eventInfo.srcElement;
    
        }
    
  3. Save the user's name in the ApplicationDataContainer for roaming settings:

    1. Call the Windows.Storage.ApplicationData.current property to get our app's ApplicationData object.
    2. Then call the ApplicationData object's roamingSettings property to get our ApplicationDataContainer for roaming settings.

    Now we can store the user's name using whatever key we want. Let's store the user name as "userName".

        function nameInputChanged(eventInfo) {
            var nameInput = eventInfo.srcElement;
    
            // Store the user's name for multiple sessions.
            var appData = Windows.Storage.ApplicationData.current;
            var roamingSettings = appData.roamingSettings;
            roamingSettings.values["userName"] = nameInput.value;
        }
    
  4. In Part 1: Create a "Hello, world" app, we used the onactivated event handler to register our event handlers. Now, let's add some more code to register the nameInputChanged event handler with the nameInput control.

        app.onactivated = function (args) {
            if (args.detail.kind === activation.ActivationKind.launch) {
                if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                    // TODO: This application has been newly launched. Initialize
                    // your application here.
                } else {
                    // TODO: This application has been reactivated from suspension.
                    // Restore application state here.
                }
                args.setPromise(WinJS.UI.processAll().then(function completed() {
    
                    // Retrieve the button and register our event handler. 
                    var helloButton = document.getElementById("helloButton");
                    helloButton.addEventListener("click", buttonClickHandler, false);
    
                    // Retrieve the input element and register our
                    // event handler.
                    var nameInput = document.getElementById("nameInput");
                    nameInput.addEventListener("change", nameInputChanged);
    
                }));
    
            }
        };
    
  5. Now let's save the state of the Rating control when it changes. All we need to do is update the event handler we created in Part 1 to save the value to our roamingSettings app container. Let's save the rating as greetingRating.

        function ratingChanged(eventInfo) {
    
            var ratingOutput = document.getElementById("ratingOutput");
            ratingOutput.innerText = eventInfo.detail.tentativeRating;
    
            // Store the rating for multiple sessions.
            var appData = Windows.Storage.ApplicationData.current;
            var roamingSettings = appData.roamingSettings;
            roamingSettings.values["greetingRating"] = eventInfo.detail.tentativeRating;
        }
    

Now, when you run the app, your name and rating is saved when you enter it in the text box. But when you relaunch the app, your name doesn't reappear. That's because we need to add code to load the state that we just saved. We do that in Step 3. But before we do that, let's learn how to save our session data.

Step 2: Save session data

Session data is temporary data that is relevant to the user’s current session in your app. A session ends when the user closes the app using the close gesture or Alt + F4, reboots the computer, or logs off the computer. In our app, the innerText of the greetingOutputdiv is session data. We restore it only if Windows suspends and terminates the app. To store session data, use the WinJS.Application.sessionState object.

When do we store our session data? Our default.js file contains an oncheckpoint event handler that we can use. This event handler gets called when Windows is about to suspend our app. However, it's best to save our session data incrementally, as it changes, just like we did for our app data, so we'll save our session data whenever the user clicks the "Say Hello" button.

To save session data

  • In default.js, update the buttonClickHandler function you created in Part 1: Create a "Hello, world!" app so that it stores the personalized greeting.

    Because the personalized greeting needs to be saved only for the session, we store it in a WinJS.Application.sessionState object. To use the sessionState object, we just add a property to it and set that property's value. Let's call the property greetingOutput.

        function buttonClickHandler(eventInfo) {
    
            var userName = document.getElementById("nameInput").value;
            var greetingString = "Hello, " + userName + "!";
            document.getElementById("greetingOutput").innerText = greetingString;
    
            // Save the session data. 
            WinJS.Application.sessionState.greetingOutput = greetingString;
        }
    

That's all we need to do to save our app's state before our app is terminated. Now we need to learn how to restore our app's state the next time the user launches the app.

Step 3: Restore the app's state

Let's take a close look at the code in the onactivated event handler that handles app activation.

The first thing the code does is obtain a reference to our app and store it in a variable named app. Then it creates a variable named activation that refers to the Windows.ApplicationModel.Activation namespace. Both of these steps are completely optional and only serve to reduce the amount of typing you have to do to refer to your app or the Windows.ApplicationModel.Activation namespace.

    var app = WinJS.Application;
    var activation = Windows.ApplicationModel.Activation;

Next, the code defines an activated event handler. The handler checks to see what type of activation just happened. It does work only if the activation is a launch activation. When another type of activation happens, the handler doesn't do anything.

(You can expand the event handler to respond to other type of activations, but that's not covered in this tutorial. For more info, see Application lifecycle.)

    app.onactivated = function (args) {
        if (args.detail.kind === activation.ActivationKind.launch) {
          

Next, the handler checks the previous execution state to see how the app was last shut down.

            if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize
                // your application here.
            } else {
                // TODO: This application has been reactivated from suspension.
                // Restore application state here.
            }

When the previous execution state is terminated, that means that, the last time the app ran, Windows successfully suspended the app and then terminated it. If the app wasn't terminated, the handler treats it as though it's being launched for the first time.

Finally, the handler calls WinJS.UI.processAll method and the code that we added to register our event handlers. It calls this code every time the app launches, regardless of the previous execution state.

            if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize
                // your application here.
            } else {
                // TODO: This application has been reactivated from suspension.
                // Restore application state here.
            }
            args.setPromise(WinJS.UI.processAll().then(function completed() {

                // Retrieve the div that hosts the Rating control.
                var ratingControlDiv = document.getElementById("ratingControlDiv");

                // Retrieve the actual Rating control.
                var ratingControl = ratingControlDiv.winControl;

                // Register the event handler. 
                ratingControl.addEventListener("change", ratingChanged, false);

                // Retrieve the button and register our event handler. 
                var helloButton = document.getElementById("helloButton");
                helloButton.addEventListener("click", buttonClickHandler, false);

                // Retrieve the input element and register our
                // event handler.
                var nameInput = document.getElementById("nameInput");
                nameInput.addEventListener("change", nameInputChanged);

            }));

        }
    };

Now that we understand what the onactivated event handler does, let's use it to restore our app's state.

To restore our app's state

  1. When the previousExecutionState is terminated, load our personalized greeting.

    In the else clause where the comments say to restore the app's state, check to see whether WinJS.Application.sessionState.greetingOutput has a value. If it does have a value, retrieve the greetingOutput div element and use it to display the greeting.

        app.onactivated = function (args) {
            if (args.detail.kind === activation.ActivationKind.launch) {
                if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                    // TODO: This application has been newly launched. Initialize
                    // your application here.
                } else {
                    // TODO: This application has been reactivated from suspension.
                    // Restore application state here.
    
                    // Retrieve our greetingOutput session state info, 
                    // if it exists. 
                    var outputValue = WinJS.Application.sessionState.greetingOutput;
                    if (outputValue) {
                        var greetingOutput = document.getElementById("greetingOutput");
                        greetingOutput.innerText = outputValue;
                    }
                }
                args.setPromise(WinJS.UI.processAll().then(function completed() {
    
                    // Retrieve the div that hosts the Rating control.
                    var ratingControlDiv = document.getElementById("ratingControlDiv");
    
                    // Retrieve the actual Rating control.
                    var ratingControl = ratingControlDiv.winControl;
    
                    // Register the event handler. 
                    ratingControl.addEventListener("change", ratingChanged, false);
    
                    // Retrieve the button and register our event handler. 
                    var helloButton = document.getElementById("helloButton");
                    helloButton.addEventListener("click", buttonClickHandler, false);
    
                    // Retrieve the input element and register our
                    // event handler.
                    var nameInput = document.getElementById("nameInput");
                    nameInput.addEventListener("change", nameInputChanged);
    
                }));
    
            }
        };
    
  2. Now we load the user name and rating. Because we want the user name and rating data to persist over multiple sessions, we stored it in the roamingSettings app data container. Let's add some code to retrieve the app data container and display the user name and rating, if they exist.

    We want this code to execute regardless of how the app shut down the last time it was run (we only need to check the previous execution state for session data), so we add it outside the if clause that checks the app's previous execution state. Let's add it inside the then handler for WinJS.UI.processAll, where we register for our events.

        app.onactivated = function (args) {
            if (args.detail.kind === activation.ActivationKind.launch) {
                if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                    // TODO: This application has been newly launched. Initialize
                    // your application here.
                } else {
                    // TODO: This application has been reactivated from suspension.
                    // Restore application state here.
    
                    // Retrieve our greetingOutput session state info, 
                    // if it exists. 
                    var outputValue = WinJS.Application.sessionState.greetingOutput;
                    if (outputValue) {
                        var greetingOutput = document.getElementById("greetingOutput");
                        greetingOutput.innerText = outputValue;
                    }
                }
                args.setPromise(WinJS.UI.processAll().then(function completed() {
    
                    // Retrieve the div that hosts the Rating control.
                    var ratingControlDiv = document.getElementById("ratingControlDiv");
    
                    // Retrieve the actual Rating control.
                    var ratingControl = ratingControlDiv.winControl;
    
                    // Register the event handler. 
                    ratingControl.addEventListener("change", ratingChanged, false);
    
                    // Retrieve the button and register our event handler. 
                    var helloButton = document.getElementById("helloButton");
                    helloButton.addEventListener("click", buttonClickHandler, false);
    
                    // Retrieve the input element and register our
                    // event handler.
                    var nameInput = document.getElementById("nameInput");
                    nameInput.addEventListener("change", nameInputChanged);
    
                    // Restore app data. 
                    var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;
    
                    // Restore the user name.
                    var userName =
                        Windows.Storage.ApplicationData.current.roamingSettings.values["userName"];
                    if (userName) {
                        nameInput.value = userName;
                    }
    
                    // Restore the rating. 
                    var greetingRating = roamingSettings.values["greetingRating"];
                    if (greetingRating) {
                        ratingControl.userRating = greetingRating;
                        var ratingOutput = document.getElementById("ratingOutput");
                        ratingOutput.innerText = greetingRating;
                    }
    
                }));
    
            }
        };
    

Now you can build and run the app, and see how it saves and restores the session state. So far, you've tested your app by running it in debug mode, and stopping it by selecting Stop Debugging in Microsoft Visual Studio. But doing this causes the app to perform a normal shutdown, and the Suspending event doesn't occur. Fortunately, Visual Studio lets you simulate suspending, terminating, and restoring an app.

To simulate suspending, terminating, and restoring an app in Visual Studio

  1. 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. Press Alt+Tab to return to Visual Studio.

  4. Open the drop down menu next to the Suspend button on the Debug Location toolbar .

    The Debug toolbar is shown by default while the debugger is running. If you don't see it, select View > Toolbars > Debug Location to show it.

  5. Select Suspend and shutdown.

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

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

Summary

Congratulations, you're done with the tutorial! You learned how to manage your app's lifecycle and save state. This tutorial showed you only two of the ways you can save app state. To learn more about the other ways you can save state, see Managing app data and Working with state efficiently.

Download the sample

Did you get stuck, or do you want to check your work? If so, download the Getting started with JavaScript sample.

Next steps

In the next part of this tutorial series, you learn how to create a more complex app. Go to Part 3: PageControl objects and navigation.

Getting started with JavaScript: Complete code for the tutorial series

Programming Windows 8 Apps with HTML, CSS, and JavaScript (ebook)