Introduction to Advanced Windows Store App Development using HTML5 and JavaScript
- 10/15/2013
- Objective 1.1: Create background tasks
- Objective 1.2: Consume background tasks
- Objective 1.3: Integrate WinMD components into a solution
- Chapter summary
- Answers
Objective 1.2: Consume background tasks
The Windows Runtime exposes many ways to interact with the system in a background task and many ways to activate a task. System triggers, time triggers, and conditions can modify the way a task is started and consumed. Moreover, a task can keep a communication channel open to send data to or receive data from remote endpoints. An application may need to download or upload a large resource, even if the user is not using it. The application can also request lock screen permission from the user to enhance other background capabilities.
Understanding task triggers and conditions
Many types of background tasks are available, and they respond to different kind of triggers for any kind of application, which can be:
MaintenanceTrigger. Raised when it is time to execute system maintenance tasks
SystemEventTrigger. Raised when a specific system event occurs
A maintenance trigger is represented by the MaintenanceTrigger class. To create a new instance of a trigger you can use the following code:
var trigger = new Windows.ApplicationModel.Background.MaintenanceTrigger(60, false);
The first parameter is the freshnessTime expressed in minutes, and the second parameter, called oneShot, is a Boolean indicating whether the trigger should be fired only one time or every freshnessTime occurrence.
Whenever a system event occurs, you can check a set of conditions to determine whether your background task should execute. When a trigger is fired, the background task will not run until all of its conditions are met, which means the code for the doWork method is not executed if a condition is not met.
All the conditions are enumerated in the SystemConditionType enum:
UserNotPresent. The user must be away.
UserPresent. The user must be present.
InternetAvailable. An Internet connection must be available.
InternetNotAvailable. An Internet connection must be unavailable.
SessionConnected. The session must be connected.
SessionDisconnected. The session must be disconnected.
The maintenance trigger can schedule a background task as frequently as every 15 minutes if the device is plugged in to an AC power source. It is not fired if the device is running on batteries.
System and maintenance triggers run for every application that registers them (and declares them in the application manifest). In addition, an application that leverages the lock screen–capable feature of the Windows Runtime can also register background tasks for other events.
An application can be placed on the lock screen to show important information to the user: the user can choose the application he or she wants on the lock screen (up to seven in the first release of Windows 8).
You can use the following triggers to run code for an application on the lock screen:
PushNotificationTrigger. Raised when a notification arrives on the Windows Push Notifications Service (WNS) channel.
TimeTrigger. Raised at the scheduled interval. The app can schedule a task to run as frequently as every 15 minutes.
ControlChannelTrigger. Raised when there are incoming messages on the control channel for apps that keep connections alive.
The user must place the application on the lock screen before the application can use triggers. The application can ask the user to access the lock screen by calling the RequestAccessAsync method. The system presents a dialog to the user asking for her or his permission to use the lock screen.
The following triggers are usable only by lock screen–capable applications:
ControlChannelReset. The control channel is reset.
SessionConnected. The session is connected.
UserAway. The user must be away.
UserPresent. The user must be present.
In addition, when a lock screen–capable application is placed on the lock screen or removed from it, the following system events are triggered:
LockScreenApplicationAdded. The application is added to the lock screen.
LockScreenApplicationRemoved. The application is removed from the lock.
A time-triggered task can be scheduled to run either once or periodically. Usually, this kind of task is useful for updating the application tile or badge with some kind of information. For example, a weather app updates the temperature to show the most recent one in the application tile, whereas a finance application refreshes the quote for the preferred stock.
The code to define a time trigger is similar to the code for a maintenance trigger:
var taskTrigger = new Windows.ApplicationModel.Background.TimeTrigger(60, true);
The first parameter (freshnessTime) is expressed in minutes, and the second parameter (called oneShot) is a Boolean that indicates whether the trigger will fire only once or at every freshnessTime occurrence.
The Windows Runtime has an internal timer that runs tasks every 15 minutes. If the freshnessTime is set to 15 minutes and oneShot is set to false, the task will run every 15 minutes starting between the time the task is registered and the 15 minutes ahead. If the freshnessTime is set to 15 minutes and oneShot is set to true, the task will run in 15 minutes from the registration time.
Time trigger supports all the conditions in the SystemConditionType enum presented earlier in this section.
Progressing through and completing background tasks
If an application needs to know the result of the task execution, the code can provide a callback for the onCompleted event.
This is the code to create a task and register an event handler for the completion event:
var builder = new Windows.ApplicationModel.Background.BackgroundTaskBuilder(); builder.name = taskName; builder.taskEntryPoint = "js\\BikeBackgroundTask.js"; var trigger = new Windows.ApplicationModel.Background.SystemTrigger( Windows.ApplicationModel.Background.SystemTriggerType.timeZoneChange, false); builder.addCondition(new Windows.ApplicationModel.Background.SystemCondition( Windows.ApplicationModel.Background.SystemConditionType.internetAvailable)) var task = builder.register(); backgroundTaskRegistration.addEventListener("completed", onCompleted);
A simple event handler, receiving the BackgroundCompletedEventArgs, can show something to the user, as in the following code, or it can update the application tile with some information.
function onCompleted(args) { backgroundTaskName = this.name; // Update the user interface }
A well-written application needs to check errors in the task execution. Because the task is already completed when the app receives the callback, you need to check whether the result is available or if something went wrong. To do that, the code can call the CheckResult method of the received BackgroundTaskCompletedEventArgs. This method throws the exception occurred during the task execution, if any; otherwise it simply returns a void.
Listing 1-4 shows the correct way to handle an exception inside a single task.
Listing 1-4. Completed event with exception handling
function onCompleted(args) { backgroundTaskName = this.name; try { args.checkResult(); // Update the user interface with OK } catch (ex) { // Update the user interface with some errors } }
Use a try/catch block to intercept the exception fired by the CheckResult method, if any. In Listing 1-4, we simply updated the user interface (UI) to show the correct completion or the exception thrown by the background task execution.
Another useful event a background task exposes is the onProgress event that, as the name implies, can track the progress of an activity. The event handler can update the UI that is displayed when the application is resumed, or update the tile or the badge with the progress (such as the percent completed) of the job.
The following code is an example of a progress event handler that updates the application titles manually:
function onProgress(task, args) { var notifications = Windows.UI.Notifications; var template = notifications.TileTemplateType.tileSquareText01; var tileXml = notifications.ToastNotificationManager.getTemplateContent(template); var tileTextElements = tileXml.getElementsByTagName("text"); tileTextElements[0].appendChild(tileXml.createTextNode(args.Progress + "%")); var tileNotification = new notifications.TileNotification(tileXml); notifications.TileUpdateManager.createTileUpdaterForApplication() .update(tileNotification); }
The code builds the XML document using the provided template and creates a tileNotification with a single value representing the process percentage. Then the code uses the CreateTileUpdaterForApplication method of the TileUpdateManager class to update the live tile.
The progress value can be assigned in the doWork function of the task using the Progress property of the instance that represents the task.
Listing 1-5 shows a simple example of progress assignment.
Listing 1-5. Progress assignment
(function () { "use strict"; // // Get a reference to the task instance. // var bgTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current; // // Real work. // function doWork() { var backgroundTaskDeferral = bgTaskInstance.getDeferral(); // Do some work bgTaskInstance.progress = 10; // Do some work bgTaskInstance.progress = 20; backgroundTaskDeferral.complete(); // Call the close function when you have done. close(); } doWork(); });
Understanding task constraints
Background tasks have to be lightweight so they can provide the best user experience with foreground apps and battery life. The runtime enforces this behavior by applying resource constraints to tasks:
CPU for application not on the lock screen. The CPU is limited to 1 second. A task can run every 2 hours at a minimum. For an application on the lock screen, the system will execute a task for 2 seconds with a 15-minute maximum interval.
Network access. When running on batteries, tasks have network usage limits calculated based on the amount of energy used by the network card. This number can be very different from device to device based on their hardware. For example, with a throughput of 10 megabits per second (Mbps), an app on the lock screen can consume about 450 megabytes (MB) per day, whereas an app that is not on the lock screen can consume about 75 MB per day.
To prevent resource quotas from interfering with real-time communication apps, tasks using the ControlChannelTrigger and the PushNotificationTrigger receive a guaranteed resource quota (CPU/network) for every running task. The resource quotas and network data usage constraints remain constant for these background tasks rather than varying according to the power usage of the network interface.
Because the system handles constraints automatically, your app does not have to request resource quotas for the ControlChannelTrigger and the PushNotificationTrigger background tasks. The Windows Runtime treats these tasks as “critical” background tasks.
If a task exceeds these quotas, it is suspended by the runtime. You can check for suspension by inspecting the suspendedCount property of the task instance in the doWork function, choosing to stop or abort the task if the counter is too high. Listing 1-6 shows how to check for suspension.
Listing 1-6. Checking for suspension
(function () { "use strict"; // // Get a reference to the task instance. // var bgTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current; // // Real work. // function doWork() { var backgroundTaskDeferral = bgTaskInstance.getDeferral(); // Do some work bgTaskInstance.progress = 10; if (bgTaskInstance.suspendedCount > 5) { return; } backgroundTaskDeferral.complete(); // Call the close function when you have done. close(); } doWork(); })();
Cancelling a task
When a task is executing, it cannot be stopped unless the task recognizes a cancellation request. A task can also report cancellation to the application using persistent storage.
The doWork method has to check for cancellation requests. The easiest way is to declare a Boolean variable in the class and set it to true if the system has cancelled the task. This variable will be set to true in the onCanceled event handler and checked during the execution of the doWork method to exit it.
The code in Listing 1-7 shows the simplest complete class to check for cancellation.
Listing 1-7. Task cancellation check
(function () { "use strict"; // // Get a reference to the task instance. // var bgTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current; var _cancelRequested = false; function onCanceled(cancelSender, cancelReason) { cancel = true; } // // Real work. // function doWork() { // Add Listener before doing any work bgTaskInstance.addEventListener("canceled", onCanceled); var backgroundTaskDeferral = bgTaskInstance.getDeferral(); // Do some work bgTaskInstance.progress = 10; if (!_cancelRequested) { // Do some work } else { // Cancel bgTaskInstance.succeeded = false; } backgroundTaskDeferral.complete(); // Call the close function when you have done. close(); } doWork(); });
In the doWork method, the first line of code sets the event handler for the canceled event to the onCanceled method. Then it does its job setting the progress and testing the value of the variable to stop working (return or break in case of a loop). The onCanceled method sets the _cancelRequested variable to true.
To recap, the system will call the Canceled event handler (onCanceled) during a cancellation. The code sets the variable tested in the doWork method to stop working on the task.
If the task wants to communicate some data to the application, it can use the local persistent storage as a place to store some data the application can interpret. For example, the doWork method can save the status in a localSettings key to let the application know whether the task has been successfully completed or cancelled. The application can then check this information in the Completed event for the task.
Listing 1-8 shows the revised doWork method.
Listing 1-8. Task cancellation using local settings to communicate information to the app
var localSettings = applicationData.localSettings; function doWork() { // Add Listener before doing any work bgTaskInstance.addEventListener("canceled", onCanceled); var backgroundTaskDeferral = bgTaskInstance.getDeferral(); // Do some work bgTaskInstance.progress = 10; if (!_cancelRequested) { // Do some work } else { // Cancel backgroundTaskInstance.succeeded = false; settings["status"] = "canceled"; } backgroundTaskDeferral.complete(); settings["status"] = "success"; // Call the close function when you have done. close(); }
Before “stopping” the code in the doWork method, the code sets the status value in the localSettings (that is, the persistent storage dedicated to the application) to canceled. If the task completes its work, the value will be completed.
The code in Listing 1-9 inspects the localSettings value to determine the task outcome. This is a revised version of the onCompleted event handler used in a previous sample.
Listing 1-9. Task completed event handler with task outcome check
function OnCompleted(args) { backgroundTaskName = this.name; try { args.checkResult(); var status = settings["status"]; if (status == "completed") { // Update the user interface with OK } else { } } catch (Exception ex) { // Update the user interface with some errors } }
The registered background task persists in the local system and is independent from the application version.
Updating a background task
Tasks “survive” application updates because they are external processes triggered by the Windows Runtime. If a newer version of the application needs to update a task or modify its behavior, it can register the background task with the ServicingComplete trigger: This way, the app is notified when the application is updated, and unregisters tasks that are no longer valid.
Listing 1-10 shows a system task that unregisters the previous version and registers the new one.
Listing 1-10. System task that registers a newer version of a task
(function () { "use strict"; function doWork() { var unregisterTask = "TaskToBeUnregistered"; var taskRegistered = false; var background = Windows.ApplicationModel.Background; var iter = background.BackgroundTaskRegistration.allTasks.first(); var current = iter.hasCurrent; while (current) { var current = iter.current.value; if (current.name === taskName) { return current; } current = iter.moveNext(); } if (current != null) { current.unregister(true); } var builder = new Windows.ApplicationModel.Background.BackgroundTaskBuilder(); builder.name = " BikeGPS" builder.taskEntryPoint = "js\\NewBikeBackgroundTask.js"; var trigger = new Windows.ApplicationModel.Background.SystemTrigger( Windows.ApplicationModel.Background.SystemTriggerType.timeZoneChange, false); builder.setTrigger(trigger); builder.addCondition(new Windows.ApplicationModel.Background.SystemCondition( Windows.ApplicationModel.Background.SystemConditionType.internetAvailable)) var task = builder.register(); // // A JavaScript background task must call close when it is done. // close(); }
The parameter of the unregister method set to true forces task cancellation, if implemented, for the background task.
The last thing to do is use a ServicingComplete task in the application code to register this system task as other tasks using the ServicingComplete system trigger type:
var background = Windows.ApplicationModel.Background; var servicingCompleteTrigger = new background.SystemTrigger( background.SystemTriggerType.servicingComplete, false);
Debugging tasks
Debugging a background task can be a challenging job if you try to use a manual tracing method. In addition, because a timer or a maintenance-triggered task can be executed in the next 15 minutes based on the internal interval, debugging manually is not so effective. To ease this job, Visual Studio background task integrated debugger simplifies the activation of the task.
Place a breakpoint in the doWork method or use the Debug class to write some values in the output window. Start the project at least one time to register the task in the system, and then use the Debug Location toolbar in Visual Studio to activate the background task. The toolbar can show only registered tasks waiting for the trigger. You can activate the toolbar using the View | Toolbars menu.
Figure 1-2 shows the background registration code and the Debug Location toolbar.
Figure 1.2. Visual Studio “hidden” Debug Location toolbar to start tasks
Figure 1-3 shows the debugger inside the doWork method.
Figure 1.3. Debugging tasks activated directly from Visual Studio
Understanding task usage
Every application has to pass the verification process during application submission to the Windows Store. Be sure to double-check the code for background tasks using the following points as guidance:
Do not exceed the CPU and network quotas in your background tasks. Tasks have to be lightweight to save battery power and to provide a better user experience for the application in the foreground.
The application should get the list of registered background tasks, register for progress and completion handlers, and handle these events in the correct manner. The classes should also report progress, cancellation, and completion.
If the doWork method uses asynchronous code, make sure the code uses deferrals to avoid premature termination of the method before completion. Without a deferral, the Windows Runtime thinks your code has finished its work and can terminate the thread. Request a deferral, use the async pattern to complete the asynchronous call, and close the deferral after the operation completes.
Declare each background task in the application manifest and every trigger associated with it. Otherwise, the app cannot register the task at runtime.
Use the ServicingComplete trigger to prepare your application to be updated.
If you use the lock screen–capable feature, remember that only seven apps can be placed on the lock screen, and the user can choose the application she wants at any time. Furthermore, only one app can have a wide tile. The application can provide a good user experience by requesting lock screen access using the RequestAccessAsync method. Be sure the application can work without the permission to use the lock screen because the user can deny access to it or remove the permission later.
Use tiles and badges to provide visual clues to the user, and use the notification mechanism in the task to notify third parties. Do not use any other UI elements in the Run method.
Use persistent storage as ApplicationData to share data between the background task and the application. Never rely on user interaction in the task.
Write background tasks that are short-lived.
Transferring data in the background
Some applications need to download or upload a resource from the web. Because of the application life-cycle management of the Windows Runtime, if you begin to download a file and then the user switches to another application, the first app can be suspended. The file cannot be downloaded during suspension because the system gives no thread and no input/output (I/O) slot to a suspended app. If the user switches back to the application, the download operation can continue, but the download operation will take more time to complete in this case. Moreover, if the system needs resources, it can terminate the application. The download is then terminated together with the app.
The BackgroundTransfer application programming interfaces (APIs) provide classes to avoid these problems. They can be used to enhance the application with file upload and download features that run in the background during suspension. The APIs support HTTP and HTTPS for download and upload operations, and File Transfer Protocol (FTP) for download-only operations. These classes are aimed at large file uploads and downloads.
The process started by one of these classes runs separate from the Windows Store app and can be used to work with resources like files, music, large images, and videos. During the operation, if the runtime chooses to put the application in the Suspended state, the capability provided by the Background Transfer APIs continues to work in the background.
The process to create a file download operation involves the BackgroundDownloader class: the settings and initialization parameters provide different ways to customize and start the download operation. The same applies for upload operations using the BackgroundUploader class. You can call multiple download/upload operations using these classes from the same application because the Windows Runtime handles each operation individually.
During the operation, the application can receive events to update the user interface (if the application is still in the foreground), and you can provide a way to stop, pause, resume, or cancel the operation. You can also read the data during the transfer operation.
These operations support credentials, cookies, and the use of HTTP headers so you can use them to download files from a secured location or provide a custom header to a custom server side HTTP handler.
The operations are managed by the Windows Runtime, promoting smart usage of power and bandwidth. They are also independent from sudden network status changes because they intelligently leverage connectivity and carry data-plan status information provided by the Connectivity namespace.
The application can provide a cost-based policy for each operations using the BackgroundTranferCostPolicy. For example, you can provide a cost policy to pause the task automatically when the machine is using a metered network and resume it if the user comes back to an “open” connection. The application does nothing to manage these situations; it is sufficient to provide the policy to the background operation.
The first thing to do to enable a transfer operation in the background is enable the network in the Package.appxmanifest file using one of the provided options in the App Manifest Designer. You must use one of the following capabilities:
Internet (Client). The app can provide outbound access to the Internet and networks in public areas, such as coffee shops, airports, and parks.
Internet (Client & Server). The app can receive inbound requests and make outbound requests in public areas.
Private Networks (Client & Server). The app can receive inbound requests and make outbound requests in trusted places, such as home and work.
Figure 1-4 shows the designer with the application capabilities needed for background data transferring.
Figure 1.4. Capabilities for background transferring
Then you can start writing code to download a file in the local storage folder. The code excerpt in Listing 1-11 starts downloading a file in the Pictures library folder.
Listing 1-11. Code to activate a background transfer
var promise = null; function downloadInTheBackground (uriToDownload, fileName) { try { Windows.Storage.KnownFolders.picturesLibrary.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.generateUniqueName) .done(function (newFile) { var uri = Windows.Foundation.Uri(uriToDownload); var downloader = new Windows.Networking.BackgroundTransfer .BackgroundDownloader(); // Create the operation. downloadOp = downloader.createDownload(uri, newFile); // Start the download and persist the promise to be able to cancel // the download. promise = downloadOp.startAsync().then(complete, error); }, error); } catch (ex) { LogException(ex); } };
The first line of code sets a local variable representing the file name to download and uses it to create the uniform resource identifier (URI) for the source file. Then the createFileAsync method creates a file in the user’s Pictures library represented by the KnownFolders.picturesLibrary storage folder using the async pattern.
The BackgroundDownloader class exposes a createDownload method to begin the download operation. It returns a DownloadOperation class representing the current operation. This BackgroundDownloader class exposes the startAsync method to start the operation.
The main properties of this class are:
The guid property, which represents the autogenerated unique id for the download operation you can use in the code to create a log for every download operation
The read-only requestedUri property, which represents the URI from which to download the file
The resultFile property, which returns the IStorageFile object provided by the caller when creating the download operation
The BackgroundDownloader class also exposes the Pause and the Resume methods, as well as the CostPolicy property to use during the background operation.
To track the progress of the download operation, you can use the provided startAsync function with a promise that contains the callback function for the progress.
Listing 1-12 shows the revised sample.
Listing 1-12. Code to activate a background transfer and log progress information
var download = null; var promise = null; function downloadInTheBackground (uriToDownload, fileName) { try { Windows.Storage.KnownFolders.picturesLibrary.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.generateUniqueName) .done(function (newFile) { var uri = Windows.Foundation.Uri(uriToDownload); var downloader = new Windows.Networking.BackgroundTransfer .BackgroundDownloader(); // Create the operation. downloadOp = downloader.createDownload(uri, newFile); // Start the download and persist the promise to // be able to cancel the download. promise = downloadOp.startAsync().then(complete, error, progress); }, error); } catch (ex) { LogException(ex); } }; function progress() { // Output all attributes of the progress parameter. LogOperation(download.guid + " - progress: "); }
In Listing 1-12, right after the creation of the download operation, the StartAsync method returns the IAsyncOperationWithProgress<DownloadOperation, DownloadOperation> interface that is transformed in a Task using the classic promise then.
This way, the callback can use the DownloadOperation properties to track the progress or to log (or display if the application is in the foreground) them as appropriate for the application.
The BackgroundDownloader tracks and manages all the current download operations; you can enumerate them using the getCurrentDownloadAsync method.
Because the system can terminate the application, it is important to reattach the progress and completion event handler during the next launch operation performed by the user. Use the following code as a reference in the application launch:
Windows.Networking.BackgroundTransfer.BackgroundDownloader.getCurrentDownloadsAsync() .done(function (downloads) { // If downloads from previous application state exist, reassign callback promise = downloads[0].attachAsync().then(complete, error, progress); }
This method gets all the current download operations and reattaches the progress callback to the first one using the attachAsync method as a sample. The method returns an asynchronous operation that can be used to monitor progress and completion of the attached download. Calling this method allows an app to reattach download operations that were started in a previous app instance.
If the application can start multiple operations, you have to define an array of downloads and reattach all of the callbacks.
One last thing to address are the timeiouts enforced by the system. When establishing a new connection for a transfer, the connection request is aborted if it is not established within five minutes. Then, after establishing a connection, an HTTP request message that has not received a response within two minutes is aborted.
The same concepts apply to resource upload. The BackgroundUploader class works in a similar way as the BackgroundDownloader class. It is designed for long-term operations on resources like files, images, music, and videos. As mentioned for download operations, small resources can be uploaded using the traditional HttpClient class.
You can use the CreateUploadAsync to create an asynchronous operation that, on completion, returns an UploadOperation. There are three overloads for this method. The MSDN official documentation provides these descriptions:
createUploadAsync(Uri, IIterable(BackgroundTransferContentPart)). Returns an asynchronous operation that, on completion, returns an UploadOperation with the specified URI and one or more BackgroundTransferContentPart objects
createUploadAsync(Uri, IIterable(BackgroundTransferContentPart), String). Returns an asynchronous operation that, on completion, returns an UploadOperation with the specified URI, one or more BackgroundTransferContentPart objects, and the multipart subtype
createUploadAsync(Uri, IIterable(BackgroundTransferContentPart), String, String). Returns an asynchronous operation that, on completion, returns an UploadOperation with the specified URI, multipart subtype, one or more BackgroundTransferContentPart objects, and the delimiter boundary value used to separate each part
Alternatively, you can use the more specific CreateUploadFromStreamAsync that returns an asynchronous operation that, on completion, returns the UploadOperation with the specified URI and the source stream.
This is the method definition:
backgroundUploader.createUploadFromStreamAsync(uri, sourceStream) .done( /* Code for success and error handlers */ );
As for the downloader classes, this class exposes the proxyCredential property to provide authentication to the proxy and serverCredential to authenticate the operation with the target server. You can use the setRequestHeader method to specify HTTP header key/value pair.
Keeping communication channels open
For applications that need to work in the background, such as Voice over Internet Protocol (VoIP), instant messaging (IM), and email, the new Windows Store application model provides an always-connected experience for the end user. In practice, an application that depends on a long-running network connection to a remote server can still work, even when the Windows Runtime suspends the application. As you learned, a background task allows an application to perform some kind of work in the background when the application is suspended.
Keeping a communication channel open is required for applications that send data to or receive data from a remote endpoint. Communication channels are also required for long-running server processes to receive and process any incoming requests from the outside.
Typically, this kind of application sits behind a proxy, a firewall, or a Network Address Translation (NAT) device. This hardware component preserves the connection if the endpoints continue to exchange data. If there is no traffic for some time (which can be a few seconds or minutes), these devices close the connection.
To ensure that the connection is not lost and remains open between server and client endpoints, you can configure the application to use some kind of keep-alive connection. A keep-alive connection is a message is sent on the network at periodic intervals so that the connection lifetime is prolonged.
These messages can be easily sent in previous versions of Windows because the application stays in the Running state until the user decides to close (or terminate) it. In this scenario, keep-alive messages can be sent without any problems. The new Windows 8 application life-cycle management, on the contrary, does not guarantee that packets are delivered to a suspended app. Moreover, incoming network connections can be dropped and no new traffic is sent to a suspended app. These behaviors have an impact on the network devices that close the connection between apps because they become “idle” from a network perspective.
To be always connected, a Windows Store app needs to be a lock screen–capable application. Only applications that use one or more background tasks can be lock screen apps.
An app on the lock screen can:
Run code when a time trigger occurs.
Run code when a new user session is started.
Receive a raw push notification from WNS and run code when a notification is received.
Maintain a persistent transport connection in the background to remote services or endpoints, and run code when a new packet is received or a keep-alive packet needs to be sent using a network trigger.
Remember, a user can have a maximum of seven lock screen apps at any given time. A user can also add or remove an app from the lock screen at any time.
WNS is a cloud service hosted by Microsoft for Windows 8. Windows Store apps can use WNS to receive notifications that can run code, update a live tile, or raise an on-screen notification. To use WNS, the local computer must be connected to the Internet so that the WNS service can communicate with it. A Windows Store app in the foreground can use WNS to update live tiles, raise notifications to the user, or update badges. Apps do not need to be on the lock screen to use WNS. You should consider using WNS in your app if it must run code in response to a push notification.
The ControlChannelTrigger class in the System.Net.Sockets namespace implements the trigger for applications that must maintain a persistent connection to a remote endpoint. Use this feature if the application cannot use WNS. For example, an email application that uses some POP3 servers cannot be modified to use a push notification because the server does not implement WNS and does not send messages to POP3 clients.
The ControlChannelTrigger can be used by instances of one of the following classes: MessageWebSocket, StreamWebSocket, StreamSocket, HttpClient, HttpClientHander, or related classes in the System.Net.Http namespace in .NET Framework 4.5. The IXMLHttpRequest2, an extension of the classic XMLHttpRequest, can also be used to activate a ControlChannelTrigger.
The main benefits of using a network trigger are compatibility with existing client/server protocols and the guarantee of message delivery. The drawbacks are a little more complex in respect to WNS and the maximum number of triggers an app can use (which is five in the current version of the Windows Runtime).
An application that uses a network trigger needs to request the lock screen permission. This feature supports two different resources for a trigger:
Hardware slot. The application can use background notification even when the device is in low-power mode or standby (connected to plug).
Software slot. The application cannot use network notification when not in low-power mode or standby (connected to plug).
This resource capability provides a way for your app to be triggered by an incoming notification, even if the device is in low-power mode. By default, a software slot is selected if the developer does not specify an option. A software slot allows your app to be triggered when the system is not in connected standby. This is the default on most computers.
There are two trigger types:
Push notification network trigger. This trigger lets a Windows Store app process incoming network packets on an already established Transmission Control Protocol (TCP) socket, even if the application is suspended. This socket represents the control channel that exists between the application and a remote endpoint, and it is created by the application to trigger a background task when a network packet is received by the application itself. In practice, the control channel is a persistent Transmission Control Protocol/Internet Protocol (TCP/IP) connection maintained alive by the Windows Runtime, even if the application is sent in the background and suspended.
Keep-alive network trigger. This trigger provides the capability for a suspended application to send keep-alive packets to a remote service or endpoint. The keep-alive packets tell the network device that a connection is still in use, to avoid closing a connection.
Before using a network trigger, the application has to be a lock screen app. You need to declare application capability and then call the appropriate method to ask the user the permission to place the application on the lock screen.
To register an application for the lock screen, ensure that the application has a WideLogo definition in the application manifest on the DefaultTile element:
<DefaultTile ShowName="allLogos" WideLogo="Assets\wideLogo.png" />
Add a LockScreen element that represents the application icon on the lock screen inside the VisualElements node in the application manifest:
<LockScreen Notification="badge" BadgeLogo="Assets\badgeLogo.png" />
You can use the App Manifest Designer, as shown in Figure 1-5, to set these properties. The Wide logo and the Badge logo options reference the relative images, and the Lock screen notifications option is set to Badge.
Figure 1.5. Badge and wide logo definition
Declare the extensions to use a background task, and define the executable file that contains the task and the name of the class implementing the entry point for the task. The task has to be a controlChannel background task type. For this kind of task, the executable file is the application itself. Apps using the ControlChannelTrigger rely on in-process activation for the background task.
The dynamic-link library (DLL) or the executable file that implements the task for keep-alive or push notifications must be linked as Windows Runtime Component (WinMD library). The following XML fragment declares a background task:
Sample of XML code
<Extensions> <Extension Category="windows.backgroundTasks" Executable="$targetnametoken$.exe" EntryPoint="ControlChannelTriggerTask.ReceiveTask"> <BackgroundTasks> <Task Type="controlChannel" /> </BackgroundTasks> </Extension> </Extensions>
You can also use the App Manifest Designer to set these extensions in an easier way, as shown in Figure 1-6.
Figure 1.6. Background task app settings
The next step is to ask the user for the permission to become a lock screen application using the RequestAccessAsync method of the BackgroundExecutionManager class of the Windows.ApplicationModel.Background namespace. The call to this method presents a dialog to the user to approve the request. See Listing 1-13.
Listing 1-13. Code to request use of the lock screen
var lockScreenEnabled = false; function ClientInit() { if (lockScreenEnabled == false) { BackgroundExecutionManager.requestAccessAsync().done(function (result) { switch (result) { case BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity: // // App is allowed to use RealTimeConnection broker // functionality even in low-power mode. // lockScreenEnabled = true; break; case BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity: // // App is allowed to use RealTimeConnection broker // functionality but not in low-power mode. // lockScreenEnabled = true; break; case BackgroundAccessStatus.Denied: // // App should switch to polling // WinJS.log && WinJS.log("Lock screen access is not permitted", "devleap", "status"); break; } }, function (e) { WinJS.log && WinJS.log("Error requesting lock screen permission.", "devleap", "error"); }); }
The BackgroundAccessStatus enumeration lets you know the user’s choice. See the comments in Listing 1-13 that explain the various states.
After your app is added to the lock screen, it should be visible in the Personalize section of the PC settings. Remember to handle the removal of the application’s lock screen permission by the user. The user can deny the permission to use the lock screen at any time, so you must ensure the app is always functional.
When the application is ready for the lock screen, you have to implement the WinMD library to perform the network operations. Remember you cannot implement a WinMD library in a Windows Store app using JavaScript. You have to create a C#, VB, or C++ WinMD component and call it from the main application.
The component code has to:
Create a control channel.
Open a connection.
Associate the connection with the control channel.
Connect the socket to the endpoint server.
Establish a transport connection to your remote endpoint server.
You have to create the channel to be associated with the connection so that the connection will be kept open until you close the control channel.
After a successful connection to the server, synchronize the transport created by your app with the lower layers of the operating system by using a specific API, as shown in the C# code in Listing 1-14.
Listing 1-14. Control channel creation and connection opening
private Windows.Networking.Sockets.ControlChannelTrigger channel; private void CreateControlChannel_Click(object sender, RoutedEventArgs e) { ControlChannelTriggerStatus status; // // 1: Create the instance. // this.channel = new Windows.Networking.Sockets.ControlChannelTrigger( "ch01", // Channel ID to identify a control channel. 20, // Server-side keep-alive in minutes. ControlChannelTriggerResourceType.RequestHardwareSlot); // Request hardware slot. // // Create the trigger. // BackgroundTaskBuilder controlChannelBuilder = new BackgroundTaskBuilder(); controlChannelBuilder.Name = "ReceivePacketTaskChannelOne"; controlChannelBuilder.TaskEntryPoint = "ControlChannellTriggerTask.ReceiveTask"; controlChannelBuilder.SetTrigger(channel.PushNotificationTrigger); controlChannelBuilder.Register(); // // Step 2: Open a socket connection (omitted for brevity). // // // Step 3: Tie the transport object to the notification channel object. // channel.UsingTransport(sock); // // Step 4: Connect the socket (omitted for brevity). // Connect or Open // // Step 5: Synchronize with the lower layer // status = channel.WaitForPushEnabled(); }
Despite its name, the WaitForPushEnabled method is not related in any way to the WNS. This API allows the hardware or software slot to be registered with all the underlying layers of the stack that will handle an incoming data packet, including the network device driver.
There are several types of keep-alive intervals that may relate to network apps:
TCP keep-alive. Defined by the TCP protocol
Server keep-alive. Used by ControlChannelTrigger
Network keep-alive. Used by ControlChannelTrigger
The keep-alive option for TCP lets an application send packets from the client to the server endpoint automatically to keep the connection open, even when the connection is not used by the application itself. This way, the connection is not cut from the underlying systems.
The application can use the KeepAlive property of the StreamSocketControl class to enable or disable this feature on a StreamSocket. The default is disabled.
Other socket-related classes that do not expose the KeepAlive property, such as MessageWebSocket, StreamSocketListener, and StreamWebSocket, have the keep-alive options disabled by default. In addition, the HttpClient class and the IXMLHttpRequest2 interface do not have an option to enable TCP keep-alive.
When using the ControlChannelTrigger class, take into consideration these two types of keep-alive intervals:
Server keep-alive interval. Represents how often the application is woken up by the system during suspension. The interval is expressed in minutes in the ServerKeepAliveIntervalInMinutes property of the ControlChannelTrigger class. You can provide the value as a class constructor parameter. It is called server keep-alive because the application sets its value based on the server time-out for cutting an idle connection. For example, if you know the server has a keep-alive of 20 minutes, you can set this property to 18 minutes to avoid the server cutting the connection.
Network keep-alive interval. Represents the value, in minutes, that the lower-level TCP stack uses to maintain a connection open. In practice, this value is used by the network intermediaries (proxy, gateway, NAT, and so on) to maintain an idle connection open. The application cannot set this value because it is determined automatically by lower-level network components of the TCP stack.
The last thing to do is to implement the background task and perform some operations, such as updating a tile or sending a toast, when something arrives from the network. The following code implements the Run method imposed by the interface:
public sealed class ReceiveTask : IBackgroundTask { public void Run(Windows.AppModel.Background.IBackgroundTaskInstance taskInstance) { var channelEventArgs = (IControlChannelTriggerEventDetails)taskInstance.TriggerDetails; var channel = channelEventArgs.ControlChannelTrigger; string channelId = channel.ControlChannelTriggerId; // Send Toast – Update Tile... channel.FlushTransport(); } }
The TriggerDetails property provides the information needed to access the raw notification and exposes the ControlChannelTriggerId of the ControlChannelTrigger class the app can use to identify the various instances of the channel.
The FlushTransport method is required if the application sends data.
Remember that an application can receive background task triggers when the application is also in the foreground. You can provide some visual clues to the user in the current page if the application is up and running.
Objective summary
An application can use system and maintenance triggers to start a background task without the need to register the application in the lock screen.
Lock screen applications can use many other triggers, such as TimeTrigger and ControlChannelTrigger.
Background tasks can provide progress indicators to the calling application using events and can support cancellation requests.
If an app needs to upload or download resources, you can use the BackgroundTransfer classes to start the operation and let the system manage its completion.
Background tasks have resource constraints imposed by the system. Use them for short and lightweight operations. Remember also that scheduled triggers are fired by the internal clock at regular intervals.
Applications that need to receive information from the network or send information to a remote endpoint can leverage network triggers to avoid connection closing by intermediate devices.
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.
Which is the lowest frequency at which an app can schedule a maintenance trigger?
2 hours.
15 minutes every hour.
7 minutes if the app is in the lock screen.
None; there is no frequency for maintenance triggers.
How many conditions need to be met for a background task to start?
All the set conditions.
Only one.
At least 50 percent of the total conditions.
All the set conditions if the app is running on DC power.
How can a task be cancelled or aborted?
Abort the corresponding thread.
Implement the OnCanceled event.
Catch an exception.
A background task cannot be aborted.
An application that needs to download a file can use which of the following? (Choose all that apply.)
The BackgroundTask class
The HttpClient class if the file is very small
The BackgroundTransfer class
The BackgroundDownloader class
The BackgroundUploader class