Advanced Windows Store App Development Using C

CHAPTER 1
Develop Windows Store apps
In this chapter, you learn how to create background tasks and implement the appropriate
interfaces for a Windows Store app. You also find out how to consume them using timing
and system triggers, request lock screen access, and create download and upload operations using the BackgroundTransfer class. The last part of the chapter is dedicated to creating and consuming WinMD components.
Objectives in this chapter:
■■
Objective 1.1: Create background tasks
■■
Objective 1.2: Consume background tasks
■■
Objective 1.3: Create and consume WinMD components
Objective 1.1: Create background tasks
Windows 8 changes the way applications run. Windows Store application life-cycle management of the Windows Runtime (WinRT) is different from previous versions of Windows: only
one application (or two in snapped view) can run in the foreground at a time. The system
can suspend or even terminate other apps from the Windows Runtime. This behavior forces
the developer to use different techniques to implement some form of background work,
such as to download a file or perform tile updates.
This section covers how to implement a background task using the provided classes and
interfaces, and how to code a simple task.
This objective covers how to:
■■
Create a background task
■■
Use the Windows.ApplicationModel.Background classes
■■
Implement the IBackgroundTask interface
1
Creating a background task
In Windows Store apps, when users work on an app in the foreground, background apps cannot interact directly with them. In fact, due to the architecture of Microsoft Windows 8 and
because of the application life-cycle management of Windows Store apps, only the foreground
app has the focus and is in the running state; the user can choose two applications in the foreground using the snapped view. All the other background apps can be suspended and even
terminated by the Windows Runtime. A suspended app cannot execute code, consume CPU
cycles or network resources, or perform disk activity such as reading or writing files.
You can define a background task that runs in the background, however, even in a
separate process from the owner app, and you can define background actions. When these
actions need to alert users about their outcomes, they can use a toast. A background task can
execute code even when the corresponding app is suspended, but it runs in an environment
that is restricted and resource-managed. Moreover, background tasks receive only a limited
amount of system resources.
You should use a background task to execute small pieces of code that require no user
interaction. You can also use a background task to communicate with other apps via instant
messaging, email, or Voice over IP (VoIP). Avoid using a background task to execute complex
business logic or calculations because the amount of system resources available to background apps is limited. Complex background workloads consume battery power as well,
reducing the overall efficiency and responsiveness of the system.
To create a background task, you have to define a class and register it with the operating system. To do this, create a Windows Metadata (WinMD) project and then create
a class in it. A background task is a public and not-inheritable class that implements the
IBackgroundTask interface defined by the Windows Runtime and is registered by using a
BackgroundTaskBuilder class instance.
MORE INFO WINMD
WinMD is covered in the section titled “Objective 1.3: Create and consume WinMD components” later in this chapter.
The IBackgroundTask imposes the implementation of a single self-explaining method: Run.
Listing 1-1 shows the implementation of this interface in a sample class.
LISTING 1-1 Class skeleton for IBackground task interface implementation
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Threading.Tasks;
Windows.ApplicationModel.Background;
namespace BikeGPS
{
2
CHAPTER 1
Develop Windows Store apps
public sealed class BikePositionUpdateBackgroundTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
// Update the GPS coordinates
}
}
}
You have to assign the event that will fire the task. When the event occurs, the operating
system calls the defined Run method. You can associate the event, called a trigger, via the
SystemTrigger or the MaintenanceTrigger class.
The code is straightforward. Using an instance of the BackgroundTaskBuilder class, associate the name of the task and its entry point by using the syntax namespace.class. The entry
point represents the background task class, as shown in the following code:
Sample of C# code
var taskName = "bikePositionUpdate";
var builder = new BackgroundTaskBuilder();
builder.Name = taskName;
builder.TaskEntryPoint = "BikeGPS.BikePositionUpdateBackgroundTask";
You must also create the trigger to let the system know when to start the background task:
builder.SetTrigger(new SystemTrigger(SystemTriggerType.TimeZoneChange, false));
The SystemTrigger class accepts two parameters in its constructor. The first parameter
of the trigger is the type of system event associated with the background task; the second,
called oneShot, tells the Windows Runtime to start the task only once or every time the event
occurs.
The complete enumeration, which is defined by the SystemTriggerType enum, is shown in
Listing 1-2.
LISTING 1-2 Types of system triggers
// Summary:
// Specifies the system events that can be used to trigger a background task.
[Version(100794368)]
public enum SystemTriggerType
{
// Summary:
//
Not a valid trigger type.
Invalid = 0,
//
// Summary:
//
The background task is triggered when a new SMS message is received by an
//
installed mobile broadband device.
SmsReceived = 1,
//
// Summary:
//
The background task is triggered when the user becomes present. An app must
//
be placed on the lock screen before it can successfully register background
//
tasks using this trigger type.
Objective 1.1: Create background tasks
CHAPTER 1
3
UserPresent = 2,
//
// Summary:
//
The background task is triggered when the user becomes absent. An app must
//
be placed on the lock screen before it can successfully register background
//
tasks using this trigger type.
UserAway = 3,
//
// Summary:
//
The background task is triggered when a network change occurs, such as a
//
change in cost or connectivity.
NetworkStateChange = 4,
//
// Summary:
//
The background task is triggered when a control channel is reset. An app must
//
be placed on the lock screen before it can successfully register background
//
tasks using this trigger type.
ControlChannelReset = 5,
//
// Summary:
//
The background task is triggered when the Internet becomes available.
InternetAvailable = 6,
//
// Summary:
//
The background task is triggered when the session is connected. An app must
//
be placed on the lock screen before it can successfully register background
//
tasks using this trigger type.
SessionConnected = 7,
//
// Summary:
//
The background task is triggered when the system has finished updating an
//
app.
ServicingComplete = 8,
//
// Summary:
//
The background task is triggered when a tile is added to the lock screen.
LockScreenApplicationAdded = 9,
//
// Summary:
//
The background task is triggered when a tile is removed from the lock screen.
LockScreenApplicationRemoved = 10,
//
// Summary:
//
The background task is triggered when the time zone changes on the device
//
(for example, when the system adjusts the clock for daylight saving time).
TimeZoneChange = 11,
//
// Summary:
//
The background task is triggered when the Microsoft account connected to
//
the account changes.
OnlineIdConnectedStateChange = 12,
}
You can also add conditions that are verified by the system before starting the background task. The BackgroundTaskBuilder class exposes the AddCondition method to add a
4
CHAPTER 1
Develop Windows Store apps
single condition, as shown in the following code sample. You can call it multiple times to add
different conditions.
builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));
The last line of code needed is the registration of the defined task:
BackgroundTaskRegistration taskRegistration = builder.Register();
Declaring background task usage
An application that registers a background task needs to declare the feature in the application manifest as an extension, as well as the events that will trigger the task. If you forget
these steps, the registration will fail. There is no <Extensions> section in the application
manifest of the Microsoft Visual Studio standard template by default, so you need to insert it
as a child of the Application tag.
Listing 1-3 shows the application manifest for the sample task implemented by the previous code (see Listing 1-2). The <Extensions> section is highlighted in bold.
LISTING 1-3 Application manifest
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest">
<Identity Name="e00b2bde-0697-4e6b-876b-1d611365485f"
Publisher="CN=Roberto"
Version="1.0.0.0" />
<Properties>
<DisplayName>BikeApp</DisplayName>
<PublisherDisplayName>Roberto</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Prerequisites>
<OSMinVersion>6.2.1</OSMinVersion>
<OSMaxVersionTested>6.2.1</OSMaxVersionTested>
</Prerequisites>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="BikeApp.App">
<VisualElements
DisplayName="BikeApp"
Logo="Assets\Logo.png"
SmallLogo="Assets\SmallLogo.png"
Description="BikeApp"
Objective 1.1: Create background tasks
CHAPTER 1
5
ForegroundText="light"
BackgroundColor="#464646">
<DefaultTile ShowName="allLogos" />
<SplashScreen Image="Assets\SplashScreen.png" />
</VisualElements>
<Extensions>
<Extension Category="windows.backgroundTasks"
EntryPoint="BikeGPS.BikePositionUpdateBackgroundTask">
<BackgroundTasks>
<Task Type="systemEvent" />
</BackgroundTasks>
</Extension>
</Extensions>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
</Capabilities>
</Package>
You have to add as many task elements as needed by the application. For example, if the
application uses a system event and a push notification event, you must add the following
XML node to the BackgroundTasks element:
<BackgroundTasks>
<Task Type="systemEvent" />
<Task Type="pushNotification" />
</BackgroundTasks>
You can also use the Visual Studio App Manifest Designer to add (or remove) a background task declaration. Figure 1-1 shows the same declaration in the designer.
FIGURE 1-1 Background task declaration in the Visual Studio App Manifest Designer
6
CHAPTER 1
Develop Windows Store apps
Enumerating registered tasks
Be sure to register the task you implement just once in your application. If you forget to check
the presence of the task, you risk registering and executing the same task many times.
To see whether a task is registered, you can iterate all the registered tasks using the
BackgroundTaskRegistration class and checking for the Value property that exposes the Name
property, as follows:
Sample of C# code
var taskName = "bikePositionUpdate";
var taskRegistered = false;
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == taskName)
{
taskRegistered = true;
break;
}
}
if (!taskRegistered)
// Register the task
Using deferrals with tasks
If the code for the Run method is asynchronous, the background task needs to use a deferral
(the same techniques of the suspend method) using the GetDeferral method, as shown in the
following code:
public async void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral _deferral = taskInstance.GetDeferral();
//
// Start one (or more) async
// Use the await keyword
//
await UpdateGPSCoordinatesAsync();
_deferral.Complete();
}
After requesting the deferral, use the async/await pattern to perform the asynchronous
work and, at the end, call the Complete method on the deferral. Be sure to perform all the
work after requesting the deferral and before calling the Complete method. Otherwise, the
system thinks that your job is already done and can shut down the main thread.
Objective 1.1: Create background tasks
CHAPTER 1
7
Thought experiment
Implementing background tasks
In this thought experiment, apply what you’ve learned about this objective. You can
find answers to these questions in the “Answers” section at the end of this chapter.
Your application needs to perform some lengthy cleaning operations on temporary
data. To avoid wasting system resources during application use, you want to perform these operations in the background. You implement the code in a background
thread, but notice that your application sometimes does not clean all the data when
the user switches to another application.
1. Why does the application not clean the data all the time?
2. How can you solve this problem?
Objective summary
■■
■■
A background task can execute lightweight action invoked by the associated event.
A task needs to be registered using WinRT classes and defined in the application
manifest.
■■
There are many system events you can use to trigger a background task.
■■
You have to register a task just once.
■■
You can enumerate tasks that are already registered.
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. How can an application fire a background task to respond to a network state
modification?
A. By using a time trigger that polls the network state every minute, and checks for
changes to this value
B. By using a SystemTrigger for the InternetAvailable event and checking to see
whether the network is present or not
C. By using a SystemTrigger for the NetworkStateChange event and using false as the
second constructor parameter (called oneShot)
D. By using a SystemTrigger for the NetworkStateChange event and using true as the
second constructor parameter
8
CHAPTER 1
Develop Windows Store apps
2. Which steps do you need to perform to enable a background task? (Choose all that
apply.)
A. Register the task in the Package.appxmanifest file.
B. Use the BackgroundTaskBuilder to create the task.
C. Set the trigger that will fire the task code.
D. Use a toast to show information to the user.
3. Is it possible to schedule a background task just once?
A. Yes, using a specific task.
B. No, only system tasks can run once.
C. Yes, using a parameter at trigger level.
D. No, only a time-triggered task can run once at certain time.
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 might 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.
This objective covers how to:
■■
Use timing and system triggers
■■
Keep communication channels open
■■
Request lock screen access
■■
Use the BackgroundTransfer class to finish downloads
Understanding task triggers and conditions
Many types of background tasks are available. They respond to different kinds of triggers for
any kind of application, which can be the following:
■■
MaintenanceTrigger Raised when it is time to execute system maintenance tasks
■■
SystemEventTrigger Raised when a specific system event occurs
Objective 1.2: Consume background tasks
CHAPTER 1
9
A maintenance trigger is represented by the MaintenanceTrigger class. This class implements the IBackgroundTrigger interface, as do other triggers available in the Windows
Runtime library. The interface in this version of the Windows Runtime declares nothing, but
will be implemented in future versions of the library. To create a new instance of a trigger, you
can use the following code:
MaintenanceTrigger taskTrigger = new MaintenanceTrigger(60, true);
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 does not
run until all its conditions are met, which means the code for the Run method is not executed
if a condition is not met.
All the conditions are enumerated in the SystemConditionType enum:
■■
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.
■■
UserNotPresent The user must be away.
■■
UserPresent The user must be present.
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 triggers 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 app on the lock screen:
■■
■■
■■
10
PushNotificationTrigger Raised when a notification arrives on the Windows Push
Notifications Service channel.
TimeTrigger Raised at scheduled intervals. 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.
CHAPTER 1
Develop Windows Store apps
It is important to note that 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 box 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; this kind of task
is useful to update 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:
TimeTrigger taskTrigger = new TimeTrigger(60, true);
The first parameter (freshnessTime) is expressed in minutes, and the second parameter
(oneShot) 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 15 minutes from
the registration time.
EXAM TIP
You cannot set the freshnessTime to a value less than 15 minutes. An exception occurs if
you try to do this.
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, it can provide a callback for
the OnCompleted event. The callback receives the instance of the BackgroundTaskRegistration
used to register the task and the corresponding event arguments.
Objective 1.2: Consume background tasks
CHAPTER 1
11
The following code creates a task and registers an event handler for the completion event:
var builder = new BackgroundTaskBuilder();
builder.Name = taskName;
builder.TaskEntryPoint = "BikeGPS.BikePositionUpdateBackgroundTask";
builder.SetTrigger(new SystemTrigger(SystemTriggerType.TimeZoneChange, false));
builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));
BackgroundTaskRegistration taskRegistration = builder.Register();
taskRegistration.Completed += taskRegistration_Completed;
A simple event handler, receiving the BackgroundTaskRegistration as the sender, can show
something to the user as in the following code, or it can update the application tile with some
information:
void taskRegistration_Completed(BackgroundTaskRegistration sender,
BackgroundTaskCompletedEventArgs args)
{
if (sender.Name == "BikeGPS.BikePositionUpdateBackgroundTask")
{
var dialog = new MessageDialog("Task BikePositionUpdateBackgroundTask
completed.");
dialog.ShowAsync();
}
}
EXAM TIP
A background task can be executed when the application is suspended or even terminated.
The OnCompleted event callback will be fired when the application is resumed from the
operating system or the user launches it again. If the application is in the foreground, the
event callback is fired immediately.
A well-written application needs to check errors in the task execution. Because the task
is already completed, you need to see whether the result is available or whether something went wrong. To do that, the code can call the CheckResult method of the received
BackgroundTaskCompletedEventArgs. This method throws the exception that occurred during
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
void taskRegistration_Completed(BackgroundTaskRegistration sender,
BackgroundTaskCompletedEventArgs args)
{
if (sender.Name == "BikeGPS.BikePositionUpdateBackgroundTask")
{
try
12
CHAPTER 1
Develop Windows Store apps
{
args.CheckResult();
var dialog = new MessageDialog(
"Task BikePositionUpdateBackgroundTask completed successfully");
dialog.ShowAsync();
}
catch (Exception ex)
{
var dialog = new MessageDialog(
"Task BikePositionUpdateBackgroundTask Errors " + ex.Message);
dialog.ShowAsync();
}
}
}
Right after the check for the task name, exposed by the Name property of the BackgroundTaskRegistration received in the event handler parameter, use a try/catch block to
intercept the exception fired by the CheckResult method, if any. In the sample presented in
Listing 1-4, we simply create a dialog box 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 user interface
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 code in Listing 1-5 is an example of a progress event handler that updates application
titles manually:
LISTING 1-5 Sample progress event handler that updates the application tile
void taskRegistration_Progress(BackgroundTaskRegistration sender,
BackgroundTaskProgressEventArgs args)
{
string tileXmlString = "<tile>"
+ "<visual>"
+ "<binding template='TileWideText03'>"
+ "<text id='1'>" + args.Progress.ToString() + "</text>"
+ "</binding>"
+ "<binding template='TileSquareText04'>"
+ "<text id='1'>" + args.Progress.ToString() + "</text>"
+ "</binding>"
+ "</visual>"
+ "</tile>";
var tileXml = new Windows.Data.Xml.Dom.XmlDocument();
tileXml.LoadXml(tileXmlString);
var tile = new Windows.UI.Notifications.TileNotification(tileXml);
Windows.UI.Notifications.TileUpdateManager.CreateTileUpdaterForApplication()
.Update(tile);
}
Objective 1.2: Consume background tasks
CHAPTER 1
13
The code in Listing 1-5 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 Run method using the Progress property of the
IBackgroundTaskInstance instance that represents the task. This instance is received directly as
the parameter of the Run method.
The following code shows a simple example of progress assignment:
public sealed class BikePositionUpdateBackgroundTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
// Update the GPS coordinates
// First operation
taskInstance.Progress = 10;
// Second operation
taskInstance.Progress = 20;
/// ....
}
}
Understanding task constraints
As stated, 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 the task:
■■
■■
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 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.
MORE INFO TASK CONSTRAINTS
Refer to the MSDN documentation at http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh977056.aspx for updated information on background task resource
constraints.
14
CHAPTER 1
Develop Windows Store apps
To prevent resource quotas from interfering with real-time communication apps, tasks
using ControlChannelTrigger and 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 ControlChannelTrigger and 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 Run method,
choosing to stop or abort the task if the counter is too high. The following code illustrates
how to check for suspension:
public sealed class BikePositionUpdateBackgroundTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
// Update the GPS coordinates
if (taskInstance.SuspendedCount > 5)
return;
}
}
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 the persistent storage.
The Run 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 Run method to exit it.
Listing 1-6 shows the simplest complete class to check for cancellation.
LISTING 1-6 Task cancellation check
public sealed class BikePositionUpdateBackgroundTask : IBackgroundTask
{
volatile bool _cancelRequested = false;
public void Run(IBackgroundTaskInstance taskInstance)
{
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
// Update the GPS coordinates
// First operation
taskInstance.Progress = 10;
Objective 1.2: Consume background tasks
CHAPTER 1
15
if (_cancelRequested == true)
return;
// Second operation
taskInstance.Progress = 20;
/// ....
}
private void OnCanceled(IBackgroundTaskInstance sender,
BackgroundTaskCancellationReason reason)
{
// you can use sender.Task.Name to identity the task
_cancelRequested = true;
}
}
In the Run 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 Run method to
stop working on the task.
If the task wants to communicate some data to the application, it can use local persistent
storage as a place to store some data the application can interpret. For example, the Run
method can save the status in a LocalSettings key to let the application know if the task has
been successfully completed or cancelled. The application can then check this information in
the Completed event for the task.
Listing 1-7 shows the revised Run method.
LISTING 1-7 Task cancellation using local settings to communicate information to the app
public void Run(IBackgroundTaskInstance taskInstance)
{
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
// Update the GPS coordinates
// First operation
taskInstance.Progress = 10;
if (_cancelRequested == true)
{
ApplicationData.Current.LocalSettings.Values["status"] = "canceled";
return;
}
// Second operation
taskInstance.Progress = 20;
16
CHAPTER 1
Develop Windows Store apps
/// ....
///
ApplicationData.Current.LocalSettings.Values["status"] = "completed";
}
Before “stopping” the code in the Run 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-8 inspects the LocalSettings value to determine the task outcome.
This is a revised version of the taskRegistration_Completed event handler used in a previous
sample.
LISTING 1-8 Task completed event handler with task outcome check
void taskRegistration_Completed(BackgroundTaskRegistration sender,
BackgroundTaskCompletedEventArgs args)
{
if (sender.Name == "BikeGPS.BikePositionUpdateBackgroundTask")
{
try
{
args.CheckResult();
var status = ApplicationData.Current.LocalSettings.Values["status"];
MessageDialog dialog;
if(status == "canceled")
dialog = new MessageDialog(
"Task BikePositionUpdateBackgroundTask canceled");
else
dialog = new MessageDialog(
"Task BikePositionUpdateBackgroundTask completed");
dialog.ShowAsync();
}
catch (Exception ex)
{
var dialog = new MessageDialog(
"Task BikePositionUpdateBackgroundTask Errors " + ex.Message);
dialog.ShowAsync();
}
}
}
The registered background task persists in the local system and is independent from the
application version.
Objective 1.2: Consume background tasks
CHAPTER 1
17
Updating a background task
Tasks “survive” application updates. 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-9 shows a task that unregisters the previous version and registers the new one.
LISTING 1-9 Using the ServicingComplete task to update a previous version of a task
using
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Threading.Tasks;
Windows.ApplicationModel.Background;
Windows.Storage;
namespace BikeGPS
{
public sealed class ServicingCompleteTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
// Look for Task v1
var task = FindTask("BikeGPS.BikePositionUpdateBackgroundTask");
if (task != null)
{
task.Unregister(true);
}
var builder = new BackgroundTaskBuilder();
builder.Name = task.Name;
builder.TaskEntryPoint = "BikeGPS.BikePositionUpdateBackgroundTask";
builder.SetTrigger(new SystemTrigger(SystemTriggerType.TimeZoneChange,
false));
builder.AddCondition(new
SystemCondition(SystemConditionType.InternetAvailable));
BackgroundTaskRegistration taskRegistration = builder.Register();
}
18
CHAPTER 1
Develop Windows Store apps
public static BackgroundTaskRegistration FindTask(string taskName)
{
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == taskName)
{
return (BackgroundTaskRegistration)(task.Value);
}
}
return null;
}
}
}
The parameter of the Unregister method set to true forces task cancellation, if implemented,
for the background task. The FindTask method is simply a helper you can use throughout the
code to find a task by name in the list of already registered tasks and return it to the caller.
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:
SystemTrigger servicingCompleteTrigger =
new SystemTrigger(SystemTriggerType.ServicingComplete, false);
Debugging tasks
Debugging a background task can be a challenging job if you try to use a manual tracing
method.
A timer or a maintenance-triggered task can be executed in the next 15 minutes based on
the internal interval, so debugging manually is not so effective. To ease this job, the Visual
Studio background task integrated debugger simplifies the task activation.
Make sure that the Windows Store application references the background task project
and the latter is configured as WinMD File. In addition, the project has to declare the background task in the application manifest. Figure 1-2 shows the complete solution with the
BikeBackgroundTask project referenced by the main application, and the project property
showing the output type set to Windows Runtime Component (also referred to as the WinMD
library or file).
Objective 1.2: Consume background tasks
CHAPTER 1
19
FIGURE 1-2 Solution structure and output type for a background task
Place a breakpoint in the Run 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. The toolbar can be activated using the
View command on the Toolbars menu.
Figure 1-3 shows the background registration code and the Debug Location toolbar.
FIGURE 1-3 Visual Studio “hidden” Debug Location toolbar to start tasks
20
CHAPTER 1
Develop Windows Store apps
Figure 1-4 shows the debugger inside the Run method.
FIGURE 1-4 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 provide a better user experience for the application in the foreground.
Objective 1.2: Consume background tasks
CHAPTER 1
21
■■
■■
■■
■■
■■
■■
■■
■■
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 Run 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 that your code has finished its work and can terminate the
thread. Request a deferral, use the async/await pattern to complete the asynchronous
call, and close the deferral after the await keyword.
Declare each background task and every trigger associated with it in the application
manifest. Otherwise, the app cannot register the task at runtime.
Use the ServicingComplete trigger to prepare your application for updating.
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 on it 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 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 I/O slot to a
suspended app. If the user switches back to the application again, the download operation
can continue but will take more time to be completed. Moreover, if the system needs resources, it can terminate the application. The download is then terminated together with the app.
The BackgroundTransfer namespace provides classes to avoid these problems. It can be
used to enhance the application with file upload and download features that run in the background during suspension. It supports HTTP and HTTPS for download and upload operations,
and File Transfer Protocol (FTP) for download-only operations. This class is aimed at large file
uploads and downloads.
22
CHAPTER 1
Develop Windows Store apps
The process started by this class runs separately from the Windows Store app and can
be used to work with resources such as 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.
NOTE BACKGROUND TRANSFER API
Background Transfer APIs work for small resources (a few kilobytes), but Microsoft suggests using the traditional HttpClient class for these kind of files.
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 UI (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 operation 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 has to do nothing to manage these situations;
it is sufficient to provide the policy to the background operation.
To enable a transfer operation in background, first 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 has 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 The app can receive inbound requests and make outbound requests in trusted places, such as at home and work.
Figure 1-5 shows the designer with the application capabilities needed for background
data transferring.
Objective 1.2: Consume background tasks
CHAPTER 1
23
FIGURE 1-5 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-10 starts downloading the session1.wmv file in the Videos Library folder.
LISTING 1-10 Code to activate a background transfer
private async void Download_Click(object sender, RoutedEventArgs e)
{
try
{
var fileName = "session1.wmv";
Uri source = new Uri("http://videos.devleap.com/" + fileName);
var targetFile = await KnownFolders.VideosLibrary.CreateFileAsync(fileName,
CreationCollisionOption.GenerateUniqueName);
24
CHAPTER 1
Develop Windows Store apps
var downloader = new BackgroundDownloader();
var downloadOperation = downloader.CreateDownload(source, targetFile);
await downloadOperation.StartAsync();
LogOperation(String.Format("Current Download {0} to {1}, {2}",
source.AbsoluteUri, targetFile.Name, downloadOperation.Guid));
}
catch (Exception ex)
{
LogOperation("Download Error", ex);
}
}
The first line of code sets a local variable representing the file name to download and uses
it to create the URI for the source file. Then the CreateFileAsync method creates a file in the
user’s Videos library represented by the KnownFolders.VideosLibrary storage folder using the
async/await pattern.
The BackgroundDownloader class exposes a CreateDownload method to begin the download operation: It returns a DownloadOperation class representing the current operation. The
BackgroundDownloader class exposes the StartAsync method to start the operation.
The main properties of this class are:
■■
■■
■■
Guid Represents the autogenerated unique id for the download operation you can
use in the code to create a log for every download operation
RequestedUri (read-only) Represents the URI from which to download the file
ResultFile Returns the IStorageFile object provided by the caller when creating the
download operation
The BackgroundDownloader class also exposes the Pause and the Resume method 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
method, transforming the result in a Task and passing the progress callback using an instance
of the System.Progress<T> class. Listing 1-11 shows the revised sample.
LISTING 1-11 Activating a background transfer and providing progress information
private async void Download_Click(object sender, RoutedEventArgs e)
{
try
{
var fileName = "session1.wmv";
Uri source = new Uri("http://video.devleap.com/" + fileName);
var targetFile = await KnownFolders.VideosLibrary.CreateFileAsync(fileName,
CreationCollisionOption.GenerateUniqueName);
Objective 1.2: Consume background tasks
CHAPTER 1
25
var downloader = new BackgroundDownloader();
var downloadOperation = downloader.CreateDownload(source, targetFile);
Progress<DownloadOperation> progressCallback = new
Progress<DownloadOperation>(DownloadProgress);
await downloadOperation.StartAsync().AsTask(progressCallback);
LogOperation(String.Format("Current Download {0} to {1}, {2}",
source.AbsoluteUri, targetFile.Name, downloadOperation.Guid));
}
catch (Exception ex)
{
LogOperation("Download Error", ex);
}
}
private void DownloadProgress(DownloadOperation downloadOperation)
{
LogOperation(String.Format("Downloading {0} to {1}, {2}",
downloadOperation.RequestedUri, downloadOperation.Progress ,
downloadOperation.Guid)
}
In the preceding code, 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 AsTask method; this method receives the
Progress<T> instance that represents the callback.
This way, the callback can receive the DownloadOperation class and can use the Guid,
RequestedUri, or ResultFile properties to track the progress or to log them (or display them if
the application is in the foreground) 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:
IReadOnlyList<DownloadOperation> downloads = await
BackgroundDownloader.GetCurrentDownloadsAsync();
if (downloads.Count > 0)
{
List<Task> tasks = new List<Task>();
foreach (DownloadOperation download in downloads)
{
26
CHAPTER 1
Develop Windows Store apps
Progress<DownloadOperation> progressCallback = new
Progress<DownloadOperation>(DownloadProgress);
await download.AttachAsync().AsTask(progressCallback);
}
}
This method iterates through all the current download operations and reattaches the
progress callback to each of them using the AttachAsync method. The method returns an
asynchronous operation that can be used to monitor progress and completion of the attached download. Calling this method enables an app to reattach download operations that
were started in a previous app instance.
Finally, you should address the time-outs 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. 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 such as files, images, music, and videos. As mentioned for download operations,
small resources can be uploaded using the traditional HttpClient class.
You can use CreateUploadAsync to create an asynchronous operation that, on completion,
returns an UploadOperation. There are three overloads for this method. The “BackgroundUploader class” page in 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:
public IAsyncOperation<UploadOperation> CreateUploadFromStreamAsync(
Uri uri,
IInputStream sourceStream
)
Objective 1.2: Consume background tasks
CHAPTER 1
27
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 pairs.
Keeping communication channels open
For applications that need to work in the background, such as VoIP, instant messaging, 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 enables the application to perform some kind of
work in the background when the application is suspended.
Keeping a communication channel open is required for an application that sends data to
or receives data from a remote endpoint. It is 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 (it can be 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, the application can be configured to use some kind of keep-alive connection (that
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 cut
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 do the following:
■■
Run code when a time trigger occurs.
■■
Run code when a new user session is started.
■■
■■
28
Receive a raw push notification from Windows Push Notification Service 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.
CHAPTER 1
Develop Windows Store apps
Remember that 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.
Windows Push Notification Service (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.
MORE INFO WINDOWS PUSH NOTIFICATION SERVICE (WNS)
For a complete discussion of WNS, see Chapter 3, “Program user interaction.”
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, HttpClientHandler, and
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 is five in the current
version of the Windows Runtime.
EXAM TIP
An application written in JavaScript cannot use a ControlChannelTrigger if it uses other
background tasks.
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 a plug).
Software slot The application cannot use network notification when not in lowpower mode or standby (connected to a plug).
Objective 1.2: Consume background tasks
CHAPTER 1
29
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 enables 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 Enables a Windows Store app to process incoming network packets on an already established TCP socket, even if the application is
suspended. This socket, which represents the control channel that exists between the
application and a remote endpoint, is created by the application to trigger a background task when a network packet is received by the application. In practice, the
control channel is a persistent TCP/IP connection maintained alive by the Windows
Runtime even if the application is sent in the background and suspended.
Keep-alive network trigger Enables a suspended application to send keep-alive
packets to a remote service or endpoint. To avoid connection cutting, the keep-alive
packets tell the network device that a connection is still in use.
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 for permission to place the application on the lock screen.
NOTE LOCK SCREEN REMOVAL
You also have to handle a situation in which the user removes the application from 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, shown in Figure 1-6, to set these properties. The
Wide Logo and the Badge Logo reference the relative images, and the Lock Screen Notifications element is set to Badge.
30
CHAPTER 1
Develop Windows Store apps
FIGURE 1-6 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
Objective 1.2: Consume background tasks
CHAPTER 1
31
file is the application. Apps using the ControlChannelTrigger rely on in-process activation for
background task. The following code declares the background task:
Sample of XML code
<Extensions>
<Extension Category="windows.backgroundTasks"
Executable="$targetnametoken$.exe"
EntryPoint="ControlChannelTriggerTask.ReceiveTask">
<BackgroundTasks>
<Task Type="controlChannel" />
</BackgroundTasks>
</Extension>
</Extensions>
The dynamic-link library (DLL) or the executable file that implements the task for keepalive or push notifications must be linked as a WinRT component (WinMD library). You can
also use the App Manifest Designer to set these extensions in an easier way, as shown in
Figure 1-7.
FIGURE 1-7 Background tasks app settings
The next step is to ask the user for permission to become a lock screen application using
the RequestAccessAsync method of the BackgroundExecutionManager class of the Windows.
ApplicationModel.Background namespace. See Listing 1-12. The call to this method presents a
dialog box to the user to approve the request.
32
CHAPTER 1
Develop Windows Store apps
NOTE PERMISSION FOR LOCK SCREEN APP
The consent dialog box prompts the user just one time. If the user denies permission for
the lock screen, you cannot prompt the user again. The user can decide later to bring the
application on the lock screen from the system permission dialog box, but the application
has no possibility to ask for the permission again.
LISTING 1-12 Requesting use of the lock screen
private Boolean lockScreenPermitted = false;
private async void LockScreenEnabling_Click(object sender, RoutedEventArgs e)
{
BackgroundAccessStatus status = await
BackgroundExecutionManager.RequestAccessAsync();
switch (status)
{
case BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity:
//
// The user chose "allow" in the dialog box. The app is added
// to the lock screen,
// can set up background tasks, and, if it has the capability,
// can use the real-time connectivity (RTC) broker. This means that
// the app can function while the device is in the connected standby state.
//
lockScreenPermitted = true;
break;
case BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity:
//
// The user chose "allow" in the dialog box. The app is added to the lock screen
// and can set up background tasks, but it cannot use the real-time connectivity
// (RTC) broker. This means that the app might not function while the device
// is in connected standby.Note that apps that do not specify RTC in their
// manifest will always demonstrate this behavior.
//
lockScreenPermitted = true;
break;
case BackgroundAccessStatus.Denied:
//
// App should switch to polling mode (example: poll for email based
// on time triggers)
//
break;
}
return;
}
The BackgroundAccessStatus enumeration lets you know the user’s choice. See the comments in Listing 1-12 that explain the various states.
Objective 1.2: Consume background tasks
CHAPTER 1
33
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 that the app is always functional.
When the application is ready for the lock screen, you have to do the following:
1. Create a control channel.
2. Open a connection.
3. Associate the connection with the control channel.
4. Connect the socket to the endpoint server.
5. Establish a transport connection to your remote endpoint server.
You have to create the channel to be associated with the connection so the connection is
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 Listing 1-13.
LISTING 1-13 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).
//
34
CHAPTER 1
Develop Windows Store apps
//
// Step 3: Bind 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 WNS. This
API enables 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 can 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 (for example,
MessageWebSocket, StreamSocketListener, and StreamWebSocket) have the keep-alive options
disabled by default. Also, 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 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.
Objective 1.2: Consume background tasks
CHAPTER 1
35
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 as 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.
Thought experiment Transferring data
In this thought experiment, apply what you’ve learned about this objective. You can
find answers to these questions in the “Answers” section at the end of this chapter.
Your application needs to upload photos to a remote storage location in the cloud.
Because photos can be greater than 10 MB, you implement a background task that
performs this operation. The process works fine, but you discover a slowdown in the
process with respect to the same code executed in an application thread (up to 10
times).
1. What is the cause of the slowdown?
2. How can you solve the problem?
36
CHAPTER 1
Develop Windows Store apps
Objective summary
■■
■■
■■
■■
■■
■■
An application can use system and maintenance triggers to start a background task
without needing to register the application in the lock screen.
A lock screen application 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 interval.
Applications that need to receive information from the network or send information to
a remote endpoint can leverage the 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.
1. What is the lowest frequency an app can schedule a maintenance trigger?
A. 2 hours
B. 15 minutes every hour
C. 7 minutes if the app is in the lock screen
D. None; there is no frequency for maintenance triggers
2. How many conditions must be met for a background task to start?
A. All the set conditions
B. Only one
C. At least 50 percent of the total conditions
D. All the set conditions if the app is running on DC power
3. How can a task be cancelled or aborted?
A. Abort the corresponding thread.
B. Implement the OnCanceled event.
C. Catch a ThreadAbortedException.
D. A background task cannot be aborted.
Objective 1.2: Consume background tasks
CHAPTER 1
37
4. An application that needs to download a file can use which of the following? (Choose
all that apply.)
A. BackgroundTask class
B. HttpClient class if the file is very small
C. BackgroundTransfer class
D. BackgroundDownloader class
E. BackgroundUploader class
Objective 1.3: Create and consume WinMD
components
The Windows Runtime exposes a simple way to create components that can be used by all the
supported languages without any complex data marshaling. A WinMD library, called Windows Runtime Component, is a component written in one of the WinRT languages (C#, VB,
C++, except JavaScript) that can be used by any supported languages.
This objective covers how to:
■■
Create a WinMD component in C#
■■
Consume a WinMD component
■■
Handle WinMD reference types
■■
Reference a WinMD component
NOTE REFERENCE
The content in this section is from Build Windows 8 Apps with Microsoft Visual C# and
Visual Basic Step by Step, written by Paolo Pialorsi, Roberto Brunetti, and Luca Regnicoli
(Microsoft Press, 2013).
Understanding the Windows Runtime and WinMD
Since its earliest version, Windows has provided developers with libraries and APIs to interact with the operating system. Before the release of Windows 8, those APIs and libraries were often complex and challenging to use, however. Moreover, while working in .NET
Framework using C# or VB.NET, you often had to rely on COM Interop, and Win32 interoperability via Platform Invoke (P/Invoke) to directly leverage the operating system. For
38
CHAPTER 1
Develop Windows Store apps
example, the following code sample imports a native Win32 DLL and declares the function
capCreateCaptureWindows to be able to call it from a .NET code:
Sample of C# code
[DllImport("avicap32.dll", EntryPoint="capCreateCaptureWindow")]
static extern int capCreateCaptureWindow(
string lpszWindowName, int dwStyle,
int X, int Y, int nWidth, int nHeight,
int hwndParent, int nID);
[DllImport("avicap32.dll")]
static extern bool capGetDriverDescription(
int wDriverIndex,
[MarshalAs(UnmanagedType.LPTStr)] ref string lpszName,
int cbName,
[MarshalAs(UnmanagedType.LPTStr)] ref string lpszVer,
int cbVer);
Microsoft acknowledged the complexity of the previously existing scenario and invested
in Windows 8 and the Windows Runtime to simplify the interaction with the native operating system. In fact, the Windows Runtime is a set of new APIs that were reimagined from the
developer perspective to make it easier to call to the underlying APIs without the complexity of P/Invoke and Interop. Moreover, the Windows Runtime is built so that it supports the
Windows 8 application development with many of the available programming languages/
environments, such as HTML5/Windows Library for JavaScript (WinJS), common language
runtime (CLR), and C++.
The following code illustrates how the syntax is clearer and easier to write, which makes
it easier to read and maintain in the future, when leveraging the Windows Runtime. In this
example, Photo is a Extensible Application Markup Language (XAML) image control:
Sample of C# code
using Windows.Media.Capture;
var camera = new CameraCaptureUI();
camera.PhotoSettings.CroppedAspectRatio = new Size(4, 3);
var file = await camera.CaptureFileAsync(CameraCaptureUIMode.Photo);
if (file != null)
{
var bitmap = new BitmapImage() ;
bitmap.SetSource(await file.OpenAsync(FileAccessMode.Read));
Photo.Source = bitmap;
}
If you prefer to write code using WinJS and HTML5, the code will be similar to the C# version, as follows:
Objective 1.3: Create and consume WinMD components
CHAPTER 1
39
Sample of JavaScript code
var camera = new capture.CameraCaptureUI();
camera.captureFileAsync(capture.CameraCaptureUIMode.photo)
.then(function (file) {
if (file != null) {
media.shareFile = file;
}
});
The Windows Runtime is a set of APIs built upon the Windows 8 operating system (see Figure 1-8) that provides direct access to all the main primitives, devices, and capabilities for any
language available for developing Windows 8 apps. The Windows Runtime is available only
for building Windows 8 apps. Its main goal is to unify the development experience of building
a Windows 8 app, regardless of which programming language you choose.
FIGURE 1-8 The Windows Runtime architecture
The Windows Runtime sits on top of the WinRT core engine, which is a set of C++ libraries that bridges the Windows Runtime with the underlying operating system. On top of the
WinRT core is a set of specific libraries and types that interact with the various tools and
devices available in any Windows 8 app. For example, there is a library that works with the
network and another that reads and writes from storage (local or remote). There is a set of
pickers to pick up items (such as files and pictures), there are several classes to leverage media
services, and so on. All these types and libraries are defined in a structured set of namespaces
and are described by a set of metadata called Windows Metadata (WinMD). All metadata
information is based on a new file format, which is built upon the common language interface
(CLI) metadata definition language (ECMA-335).
Consuming a native WinMD library
The WinRT core engine is written in C++ and internally leverages a proprietary set of data
types. For example, the HSTRING data type represents a text value in the Windows Runtime.
In addition, there are numeric types such as INT32 and UINT64, enumerable collections represented by IVector<T> interface, enums, structures, runtime classes, and many more.
40
CHAPTER 1
Develop Windows Store apps
Download PDF
Similar pages