Designing Windows Store Apps with HTML and JavaScript: An Overview

  • 7/18/2014

Objective 1.3: Design and implement Process Lifetime Management (PLM)

When working with regular desktop applications, you are used to launching and closing them yourself. When you switch to another application, other running applications stay in memory, and you can easily switch back to them. When your computer starts running slowly, you start closing applications and maybe even open Task Manager to check what’s happening.

Windows Store apps behave differently. Microsoft doesn’t want users to bother with actively closing applications, so it created Process Lifetime Management (PLM) for Windows Store apps that manages the lifetime of an app without any user intervention.

The life cycle of your app is the foundation on which you build. Make sure that you get it right. You can have a beautiful app, but when it doesn’t behave as users expect, you will lose them.

Choosing a state management strategy

Windows Store apps can be launched and terminated in a couple of different ways. Understanding the application life cycle and anticipating it in your app lead to a better user experience in which your app naturally behaves as a user would expect.

Apps start their lives in the not running state. You launch the app by clicking the tile on the Start screen; your app then displays its splash screen, loads data, and begins running.

That’s the easy track. In reality, however, a lot more can happen. Figure 1-10 shows the typical life cycle of an app.

FIGURE 1-10

FIGURE 1-10 The life cycle of an app

When your app is in the suspended state, it consumes less memory than it consumes in the running state and it doesn’t get scheduled for CPU time, which saves power to enable longer battery times on tablets and laptops. Although Windows tries to keep as many apps as possible in the suspended state, when the operating system is running low on resources (typically memory), Windows starts terminating apps that haven’t been used for some time.

Choosing your state management strategy comes down to understanding the life cycle of your app and responding appropriately. What does this mean? When users (game or blog readers, for example) leave your app and then return to it, they expect to come back at the same point with the same settings as when they left.

In the meantime, maybe Windows suspended your app or even terminated it. However, the users don’t know the situation and want to continue working with your app. So you have to respond to the events such as suspension or resumption and make sure that you save the correct state and restore it whenever necessary so users don’t notice anything.

You can take this process one step farther. Because apps can be installed on multiple devices, you should accommodate a user switching between those devices when using your app. You need to save all the details of the users’ actions to an external source and reload them whenever an app launches on a device. Windows helps you by automatically roaming data to all user devices so that you can share state across devices and provide a seamless experience.

Handling the onactivated event

Your app can be activated in a variety of ways. The most obvious one, of course, is a user directly launching it by clicking the app tile on the Start screen. There are also many more ways to activate apps. If you use toast notifications (which are discussed in more detail in Chapter 4, “Program user interaction”), a user can launch your app by clicking a notification. If you are implementing contracts (see Chapter 2, “Develop Windows Store apps,” for more details), a user can launch your app with the Search or Share charms, by file type, or with URI associations.

These activation events require a different strategy. Fortunately, the Visual Studio templates give you some boilerplate code that you can use to react to those events. After you create a new app from the Blank App template, you see the following code in the default.js file:

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());
    }
};

The code shows how to subscribe to the onactivated event of your WinJS application. Inside the event handler, you can see whether your app is newly launched or you are resuming from a suspended state.

This state affects the steps you need to take. If the app is newly launched, initialize the app and show its home screen to users. If users return to your app, make sure that they return to the exact same point.

If the UI content has changed since the app was suspended, you need to load the new data and update your UI accordingly. Your app’s activated event is running while Windows shows the splash screen, which is why you should make sure that your initialization is as fast as possible.

Your app can also be associated with a certain file type or a URI. Associating your app with a file type is configured in the application manifest.

Figure 1-11 shows the Manifest Designer with the File Type Association configured for files that have an extension of .my.

FIGURE 1-11

FIGURE 1-11 The Manifest Designer dialog box showing the File Type Associations

After configuring these settings, launch the app from Visual Studio to register your new file type with Windows. You can then create a new text file and change the extension to .my. Double-click the new file to launch your app.

During the activated event of your app, you can see whether the app is launched from an associated file:

if (args.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.file) {
    var file = args.detail.files[0];
    Windows.Storage.FileIO.readTextAsync(file).then(function (text) {
    });

    // The number of files received is eventArgs.detail.files.size
    // The first file is eventArgs.detail.files[0].name
}

This code checks to see whether ActivationKind is of type file. If so, the arguments passed to your activated event handler contain a details.files property that contains information about the file or files that a user selected when launching your app. In this example, you are dealing with a plain text file, so you can pass it to Windows.Storage.FileIO.readTextAsync and read the text content of the file.

Your app can also be activated from a URI. One example is the Windows Store. By navigating to a URI of the form ms-windows-store:PDP?PFN=, you launch the Windows Store and navigate to the specified Package Family Name. The ms-windows-store part of the URI is called the protocol.

You can add your own protocols to the app to associate it with specific URIs. Figure 1-12 shows the Manifest Designer with a newly added protocol of mypro.

FIGURE 1-12

FIGURE 1-12 The Manifest designer dialog box showing the Protocol declaration

Of course, it’s important to configure a logo and descriptive display name, after which you can launch the app to register your protocol with Windows. Opening Windows Explorer and navigating to mypro://content launches your app.

Just as with File Type Associations, you can see whether the app is launched from a URI in your activated event:

if (args.detail.kind === activation.ActivationKind.protocol) {
    var uri = args.detail.uri;
    var rawUri = uri.rawUri;
}

The args.detail.uri property contains information about the URI that launched your app. It is up to you to parse the URI and take appropriate action.

Remember that both the files and URIs that launch your app can be harmful. You should never trust the input a user gives you and always use security measures when dealing with external input.

Handling the suspend event (oncheckpoint)

When a user switches to another app, Windows suspends your app after a couple of seconds, which enables the user to immediately switch back to your app without it having to do any work.

Whenever Windows notices that the user isn’t coming back right away, your app is suspended. Windows then raises the checkpoint event. In this event, you can save any user state that you want to restore when the app would be resumed from termination.

The syntax of subscribing to the checkpoint event is as follows:

    app.oncheckpoint = function (args) {
    };

Inside the function, you can save any state or other data that you want to restore when the app moves from terminated to running in the WinJS.Application.sessionState object. The content of this object is serialized to your local appdata folder. When the app is activated again from the terminated state, the sessionState object is rehydrated from your local appdata folder. You can then use the data inside the sessionState object to reinitialize your app.

This process can be as easy, as the following example shows:

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 {
            var value = WinJS.Application.sessionState.value;
        }
        args.setPromise(WinJS.UI.processAll());
    }
};

app.oncheckpoint = function (args) {
    WinJS.Application.sessionState.value = 42;
};

When your app goes into suspension, a value of 42 is saved inside your sessionState object. When the app launches from a terminated state, the value is retrieved from the object.

You can also use the WinJS.Application object directly to write and read state from the local, temp, or roaming folders. Writing directly to those folders can be useful if your data can’t be directly serialized to a string or if you want specific control over the location of your data.

When using any asynchronous actions inside a checkpoint event, you have to signal it to the operating system. Windows assumes that you saved all your state when the checkpoint events returns, so it doesn’t give your app any CPU time. To avoid losing CPU time with asynchronous operations, you can use the args.setPromise() method, as in the activated event.

Remember that you never get more than five seconds. If you don’t return from the checkpoint method or finish all your asynchronous operations within five seconds, your app is terminated.

Preparing for app termination

When your app goes from running to suspended, you receive a notification from the Windows operating system. But when your app goes from suspended to terminated, your app doesn’t receive a notification. This is by design and is actually quite logical.

Your app is terminated because the operating system is low on resources. Activating your app only to prepare itself for termination could become troublesome because the sole act of activating the app uses resources. And when your app tries to save some state to disk or call web services, even more memory is used.

To save resources, your app doesn’t get called when your app terminates. Instead, you should do all your work in the checkpoint event discussed in the previous section. Then whenever your app goes from suspended to terminated, you have saved all the required state.

Using background tasks

But what if you want to keep running when the user closes your app? You can do so by using background tasks. You can request Windows to grant permission to execute code in the background by using the application Manifest Designer.

Figure 1-13 shows the application Manifest Designer with a BackgroundTask extension.

FIGURE 1-13

FIGURE 1-13 The Manifest Designer dialog box showing the Background Tasks declaration

The background task is configured to trigger on a system event and on a timer. When the Background Task is triggered, it launches the JavaScript file js\backgroundtask.js. Your background task consists of two parts: the actual task and the code to register your task.

Begin with registration. The following method lets you register a background task:

function registerTask(taskEntryPoint, taskName, trigger, condition) {

    var builder = new Windows.ApplicationModel.Background.BackgroundTaskBuilder();

    builder.name = taskName;
    builder.taskEntryPoint = taskEntryPoint;
    builder.setTrigger(trigger);

    if (condition !== null) {
        builder.addCondition(condition);
        builder.cancelOnConditionLoss = true;
    }

    var task = builder.register();

    task.addEventListener("progress", new BackgroundTaskSample.progressHandler(task).onProgress);
    task.addEventListener("completed", new BackgroundTaskSample.completeHandler(task).onCompleted)

    var settings = Windows.Storage.ApplicationData.current.localSettings;
    settings.values.remove(taskName);
};

This method takes a parameter that points to your JavaScript file that contains the actual task, a name, the trigger you want to use, and a condition that determines whether the task should run. You can call the method like this:

registerTask("js\\backgroundtask.js",
                            "SampleJavaScriptBackgroundTask",
                            new Windows.ApplicationModel.Background.SystemTrigger(
                                      Windows.ApplicationModel.Background.SystemTriggerType.timeZoneChange, false),
                            null);

This code registers a background task that runs whenever users change their time zone without any other conditions.

Triggers can be any of the following:

  • SmsReceived The background task is triggered when a new Short Message Service (SMS) message is received by an installed mobile broadband device.
  • UserPresent The background task is triggered when the user becomes present.
  • UserAway The background task is triggered when the user becomes absent.
  • NetworkStateChange The background task is triggered when a network change occurs, such as a change in cost or connectivity.
  • ControlChannelReset The background task is triggered when a control channel is reset.
  • InternetAvailable The background task is triggered when the Internet becomes available.
  • SessionConnected The background task is triggered when the session is connected.
  • ServicingComplete The background task is triggered when the system has finished updating an app.
  • LockScreenApplicationAdded The background task is triggered when a tile is added to the lock screen.
  • LockScreenApplicationRemoved The background task is triggered when a tile is removed from the lock screen.
  • TimeZoneChange The background task is triggered when the time zone changes on the device (for example, when the system adjusts the clock for daylight savings time [DST]).
  • OnlineIdConnectedStateChange The background task is triggered when the Microsoft account connected to the account changes.
  • BackgroundWorkCostChange The background task is triggered when the cost of background work changes.

Remember that for triggers such as user presence and others, your app must also be visible on the lock screen.

If you are not interested in every trigger change, you can add additional conditions to your background task:

  • UserPresent Specifies that the background task can run only when the user is present. If a background task with the UserPresent condition is triggered and the user is away, the task doesn’t run until the user is present.
  • UserNotPresent Specifies that the background task can run only when the user is not present. If a background task with the UserNotPresent condition is triggered and the user is present, the task doesn’t run until the user becomes inactive.
  • InternetAvailable Specifies that the background task can run only when the Internet is available. If a background task with the InternetAvailable condition is triggered and the Internet is not available, the task doesn’t run until the Internet is available again.
  • InternetNotAvailable Specifies that the background task can run only when the Internet is not available. If a background task with the InternetNotAvailable condition is triggered, and the Internet is available, the task doesn’t run until the Internet is unavailable.
  • SessionConnected Specifies that the background task can run only when the user’s session is connected. If a background task with the SessionConnected condition is triggered and the user session is not logged on, the task runs when the user logs on.
  • SessionDisconnected Specifies that the background task can run only when the user’s session is disconnected. If a background task with the SessionDisconnected condition is triggered and the user is logged on, the task runs when the user logs off.
  • FreeNetworkAvailable Specifies that the background task can run only when a free (nonmetered) network connection is available.
  • BackgroundWorkCostNotHigh Specifies that the background task can run only when the cost to do background work is low.

After configuring the triggers and conditions, the only thing you need is the actual task. In the previous example, you pointed to a specific JavaScript file: js/backgroundtask.js.

A simple background task can look like this:

(function () {
    "use strict";

    var cancel = false,
        progress = 0,
        backgroundTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current,
        cancelReason = "";

    function onCanceled(cancelEventArg) {
        cancel = true;
        cancelReason = cancelEventArg.type;
    }
    backgroundTaskInstance.addEventListener("canceled", onCanceled);

    function onTimer() {
        var key = null,
            settings = Windows.Storage.ApplicationData.current.localSettings,
            value = null;

        if ((!cancel) && (progress < 100)) {
            setTimeout(onTimer, 1000);
            progress += 10;
            backgroundTaskInstance.progress = progress;
        } else {
            backgroundTaskInstance.succeeded = (progress === 100);
            value = backgroundTaskInstance.succeeded ? "Completed" : "Canceled with
reason: " + cancelReason;

            key = backgroundTaskInstance.task.name;
            settings.values[key] = value;

            close();
        }
    }
    setTimeout(onTimer, 1000);
})();

This code is a self-enclosing function that contains the task, which consists of the onTimer method that does the actual work. It also has a cancel event handler to see whether the task should be canceled.

The Windows.UI.WebUI.WebUIBackgroundTaskInstance.current property gives you access to the background task framework of Windows. You can check for cancellation and signal success or failure by using this object.

The call to close at the end of your task is required to signal that your task is done.

Background tasks are not meant to be used for long-running tasks. They should be used to respond to changes in the environment and run short tasks on a timer.

Checking the ActivationKind and previous state

For the exam, make sure that you understand the reasoning behind the ActivationKind enumeration and the value for the previous state of your app.

When you look at the activated event, you see both the kind and the previous state used:

app.onactivated = function (args) {
    if (args.detail.kind === activation.ActivationKind.launch) {
        if (args.detail.previousExecutionState !==
                    activation.ApplicationExecutionState.terminated) {
        } else {
        }
        args.setPromise(WinJS.UI.processAll());
    }
};

ActivationKind can have a lot of different values:

  • Launch The user launched the app or tapped a content tile.
  • Search The user wants to search with the app.
  • ShareTarget The app is activated as a target for share operations.
  • File An app launched a file whose file type is registered to be handled by this app.
  • Protocol An app launched a URL whose protocol is registered to be handled by this app.
  • FileOpenPicker The user wants to pick files provided by the app.
  • FileSavePicker The user wants to save a file and selects the app as the location.
  • CachedFileUpdater The user wants to save a file for which the app provides content management.
  • ContactPicker The user wants to pick contacts.
  • Device The app handles AutoPlay.
  • PrintTaskSettings The app handles print tasks.
  • CameraSettings The app captures photos or video from an attached camera.
  • RestrictedLaunch The user launched the restricted app.
  • AppointmentsProvider The user wants to manage appointments provided by the app.
  • Contact The user wants to handle calls or messages for the phone number of a contact provided by the app.
  • LockScreenCall The app launches a call from the lock screen. If the user wants to accept the call, the app displays its call UI directly on the lock screen without requiring the user to unlock. A lock screen call is a special type of launch activation.

Most values come from integrating with the operating system. When you start implementing contracts, your app can be activated in a lot of different scenarios that you need to handle. Chapter 2 provides more detail on implementing contracts.

If the user explicitly closed your app, you can assume that there was some kind of error that the user wanted to correct. Restoring the state to the point where the user closed your app doesn’t help. Instead, you should do a clean initialize of your app.

You can use the args.detail.previousExecutionState property to check the previous state of the app. It can be one of the following values:

  • NotRunning The app is not running.
  • Running The app is running.
  • Suspended The app is suspended.
  • Terminated The app was terminated after being suspended.
  • ClosedByUser The app was closed by the user.

A previous state of NotRunning, which is the most common one, occurs whenever a user launches your app for the first time. It can happen after installing the app, but also after a computer reboot or when switching accounts.

A previous state of Running means that your app is already running, but one of its contracts or extensions is activated.

Suspended happens whenever Windows kept your app in memory but didn’t assign any CPU to it. When this happens, you might want to update any on-screen content to make sure everything is up to date.

Terminated is the state you learned about in the previous sections. Whenever Windows determines that your app should be removed from memory, it terminates your app. When resuming from a terminated state, you need to reload all state, which can be done from the sessionState object or from an external web service (when you want to make sure that everything is up to date).

ClosedByUser has a different behavior. Whenever the user forcefully closes your app (through Alt+F4 or the close gesture) and returns within 10 seconds, you do a clean startup because Windows assumes that there was an error, and the user restarts the app. When the user takes longer to return, you need to restore state so the user can continue.

Objective summary

  1. Windows Store apps go through a life cycle in which an app can be not running, running, suspended, or terminated.
  2. The activated event is important for initializing your app for users.
  3. The checkpoint event allows you to save any user state and data before your app gets suspended and possibly terminated.
  4. When your app gets activated, it is important to know the reason why your app is activated and its previous state.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter.

  1. You are creating a game as a Windows Store app that features real-time action, and you are thinking about state management. Which of the following statements is true about state management? (Choose all that apply.)

    1. For a game, you don’t have to consider state management.
    2. You need to implement the activated event.
    3. You need to implement the checkpoint event.
    4. You need to implement the terminated event.
  2. A user closes your Windows 8.1 app by pressing Alt+F4. What should you do when the user returns the following day?

    1. Do a fresh start of the app because the user forcefully closed the app.
    2. Reload all user state and continue as if the user never left.
    3. Show a dialog box that asks whether the user wants to continue or start over.
    4. Restart the application behind the scenes to force a clean start.
  3. You want to restore any saved state when the app resumes. Which event do you use?

    1. Ready
    2. Loaded
    3. Checkpoint
    4. Activated