Background Tasks and App Lifetime - Programming Windows Store Apps with C# (2014)

Programming Windows Store Apps with C# (2014)

Chapter 14. Background Tasks and App Lifetime

In this chapter we’re going to look at how we can make portions of our code run even when our app is not—a capability known as background tasks.

Background tasks are easy enough to work with, but they are one of the areas of Windows Store apps that is on the “difficult” end of the spectrum in terms of features in the API. This is because, with mobile, preserving battery life is paramount. Applications that can wake up and do whatever they like in the background are a problem, because without any form of control it’d be easy enough for the user to install some innocuous app, only to find that battery life on the device had halved without any real idea as to why.

In Windows 8/Windows RT, there are so many controls and restrictions on what background tasks can do that you could describe them as being openly hostile to apps that need to run in the background. The reason for this is that background tasks are designed to keep the device “fresh,” but to do so within the context of a consumer/retail usage model. With LOB apps there is much more pressure on time, and it’s this part that’s not fantastically well served by the background tasks implementation as it stands at the time of writing. For example, a 10-minute delay in updates on a game is fine. If you have employees doing 20 jobs a day coordinated by your app with a 10-minute delay, however, that’s a 3.3-hour delay per operative inserted into each working day just for idle time.

It’s in this context that we’re going to discuss background tasks: the classic “field worker” scenario. An operative is mobile all day, but from time to time will receive notifications of new jobs through his device. He will also complete jobs and have to send updates back to base. Connectivity may be sketchy, so although the user can click Add New Report (in our case) and the report will save locally, it could be many minutes or hours before the connection is sufficient to send the data back. The business usually requires data to be sent back in a timely manner—it’s unusual that these systems in the real world are rigged to work on an “it’s OK to sync when you get back to base” basis. The business will expect progress returned wirelessly throughout the day.

To this end, we’re going to create a “synchronization” background task that will send up any pending updates, and download any new jobs.

What we’ll do first is talk about app lifetime in Windows 8 and Windows RT. We’ll then tackle building the background tasks that we need for our StreetFoo app. We’ll look first at the general structure of the API and then go on to build our actual synchronization routines.

App Lifetime

From first principles, you’d expect app lifetime to be quite a big topic in Windows Store app development. Practically, though, there’s hardly anything to it, as the rules are so simple. If an app’s running, it’s taking up battery power—and the best approach that Windows can take is to make sure that if it’s not being used, it’s not running.

Any opportunity it has to do so, Windows will stop scheduling time for your process to run. When main memory comes under pressure, your app’s memory working set can be dumped to disk and your process unloaded. When the user selects your app again, a new process is established, your working set reloaded, and CPU time scheduled to you. I offer this part as helpful information—as a developer you don’t need to code anything special to support it, and as a user you shouldn’t even notice that it’s happening.

In total, you have three possible states for your app: Running, Suspended, and NotRunning. You get events, which you can access via Application and therefore your individual App class for Suspending (going from running to suspended) and for Resuming (going from suspended to running). You won’t be told when you get dumped out of memory—you just get killed off. However, if you are running, you will go through the suspend phase before you do get killed off. Therefore, this is a good time to do any cleaning up or persisting of data that you need to do. (In fact, it’s your only time.)

There is an important restriction with regards to suspending—you only get five seconds to do your work. If you take longer than that, you’ll be unceremoniously killed. (And we’re about to talk about CPU time, so I’m getting ahead of myself. This five seconds is as you would count them on your watch.)

NOTE

While we’re here, it’s worth mentioning the OnActivated and OnLaunched methods in Application. OnLaunched is called if the user launches your task from the Start screen via a tile. OnActivated is called if you start for any other reason. (If you recall, in a few places throughout this book we’ve had to do different behavior when we get launched by things like file association, share charms, etc.)

Underpinning all of this design is a new feature in Windows 8 and Windows RT called connected standby (CS). Prior to this computers were either “running normally,” in “sleep mode” (main memory powered and controlled by the CPU, but no OS processes scheduled, no devices running), in “hibernate mode” (main memory contents entirely swapped to disk, computer off), or just “switched off.”

CS is an operating system feature, and not necessarily a power management feature. In CS the system is still running as normal, it’s just that Windows will stop doing as much as possible. If the hardware is appropriate (and I’ll get to that), when the screen is off Windows is considered to be in CS. Windows Store apps are suspended as one step. As another step, normal Windows processes are suspended. There’s this rather beautifully named step in the whole process called quiescing. This is why it’s called CS—Windows is still running, as is the entire networking stack, hence Windows is connected. Your background tasks will still run in this mode, and when they are, everything will appear normal. After all, you’re sandboxed off from everything so you never really know what the state of the rest of the system is in any case. Everyone else could be fast asleep, or processing a hard workload. You don’t know, nor should you care.

Hardware-wise, in order for hardware to be CS-capable, the rules are that it has to have a certain flag set in firmware, it can’t have spinning disks (as they could wake up to find themselves under heavy motion, which could break them), and it must be able to be cooled sufficiently via passive cooling. (One of the things that will wake the system is a thermal warning, so any problem there means the machine will hibernate and shut down fully.) For completeness, there is also a special capability you need on network adapters, but that’s just a detail point.

In all, CS is a really decent Windows 8 feature, and what you’re about to read may seem restrictive and strange—especially CPU quotas—but I hope as you reflect back on CS you’ll realize why it’s been built as it has.

Background Tasks API

There are three basic concepts that you need to understand about background tasks. Learning how to implement background tasks to good effect is a matter of learning the subtleties and nuances of these three concepts. We’ll talk about all of these aspects in more detail throughout the chapter.

§ First, you have a CPU usage quota. The idea here is that you are given an amount of CPU to use—n sections every m minutes. Go over this, and your task is suspended until you “save up” more quota.

§ Second, you have a set of triggers that you can use to kick off your tasks. Triggers also have conditions. For example, you can create a trigger to run every 15 minutes without conditions, or schedule the same thing but only on the condition that a user is logged on when it’s time to run.

§ Third, background tasks are executed outside of the process of the main app, regardless of whether the app is running or not. (This is a simplification—there are instances where they run inside your own app’s process, but only certain types of tasks can do that, and those tasks are outside the scope of this book.) Restrictions also exist in that the class containing the task to execute must be in a WinRT component, not a normal .NET assembly. (WinRT components have the .winmd filetype.) We haven’t looked this topic at all so far, but we will in this section.

Let’s look at each of these three concepts in turn.

CPU Usage Quota

I wanted to present this concept first because it’s the one you need to have the best grasp of, but it’s also the most woolly of all of them.

CPU usage quotas are quoted as follows:

§ If you’re running on the “lock screen,” you get two seconds of CPU time every fifteen minutes.

§ If you’re not running on the “lock screen,” you get one second of CPU time every two hours.

The first thing that needs defining here is CPU time. This is not the same measurement as SI units of time that you’d measure on your watch. This is often known as “clock on the wall” time.

For example, a method can start, run in five seconds of “clock on the wall time,” and still come in under quota. CPU time is measured by Windows as time when the code is not blocking, and as we’ve seen with the asynchrony implementation throughout this book, there are plenty of times when your code is blocking. (Deep down, it relies on the Windows internal process timing instrumentation to work out how much time you’ve used.)

Simultaneously, this makes the CPU problem less pressing, but much weirder because it becomes harder to measure and understand without deep inspection. Coming back to connected standby, this restriction exists because the device never really ends up being “off.” A laptop asleep in a bag being toted from site to site is in sleep, hibernate, or “off” mode. In any of those modes, user code doesn’t run. As an OS designer, Microsoft needed to make sure that each app could only take “light sips” from the CPU when in CS.

Your strategy, then, should be to maximize the quota. The only ways you can do this are to a) do less, and b) put yourself on the lock screen. You should also note that the quota is shared across all of the background tasks tied to your app package—you don’t get more quota by registering more tasks.

Do less

This becomes a normal optimization problem. If you have to download new work from a server, the background task would be better used to grab the data from the server, spool it to disk, and then stop as opposed to downloading it and processing it into the database. When the app resumes and you have no CPU quota in play, you can pick up the spooled data, and then update the database, complete all of the local processing, etc. (We’ll implement this approach in our sync routine.)

Putting yourself on the lock screen

This is a good way of octupling your quota, but with much less direct control. Tasks on the lock screen appear as icons that can provide immediate feedback even if the device is off—for example, reporting on new emails or new calendar appointments. However, there are problems.

First, you can only request that the user puts your app on the lock screen—you can’t make the user do it and you can’t force it to happen. (We’ll see this later during the implementation.) Second, you can only have seven apps on the lock screen in total. Third, if you go onto the lock screen, the user can remove you, and you don’t get to keep asking her whether you can go back on it. Nor do you get to query whether you are still on the lock screen.

From a consumer app perspective, all of this is sensible. However, from a LOB app perspective, this could ideally do with less restriction. Having to train employees of the business that paid for the app not to remove LOB apps from the lock screen is pretty silly.

Network constraints

Just as there are CPU constraints, there are also network constraints. Whereas the CPU constraints/quota apply whether the device is on AC power or battery power, network constraints only apply when on battery power.

I’m not going to go into detail on this; the restrictions are fairly roomy, and if you’re doing “normal” communications (i.e., just shuttling bits of control data back and forth) you’re unlikely to hit it. However, if you are planning to do large transfers on background tasks, you’ll need to think about network constraints.

Triggers and Conditions

Now that you understand the CPU quota, we can look at triggers.

MaintenanceTrigger

This is the simplest type of trigger to understand. You specify an interval (which can’t be less than 15 minutes) and, provided that the device has AC power, the trigger will run. You don’t have to be on the lock screen, but to reiterate, you will need AC power.

TimeTrigger

This is like a MaintenanceTrigger, but you don’t need AC power and you do need to be on the lock screen. Being on the lock screen creates wrinkles in your implementation as per the previous explanation. Specifically, even for sideloaded LOB apps, you can’t guarantee that you’re on the lock screen. Again, the minimum interval is 15 minutes. (We’ll talk about sideloading in Chapter 15.)

SystemTrigger

This is raised when certain system events happen, such as a user logging in or out, the Internet becoming available, and so on. I’m not going to repeat the MSDN documentation for this trigger in these pages, but it’s worth having a look to see the sorts of things that you can do. (We’ll use the InternetAvailable one in our example, though.) Some event types only apply if you’re on the lock screen.

PushNotificationTrigger

This can be used to receive raw notifications. We didn’t talk about these much in Chapter 5. To handle this sort of trigger, you’ll need to be on the lock screen. (We’re not going to go into detail on this here—refer to the MSDN for more information.)

ControlChannelTrigger

We’re not going to talk about this one. This is for specialized networking activities that are beyond the scope of this book.

NOTE

It’s the PushNotificationTrigger and ControlChannelTrigger that can be configured to run within your app’s process. All of the others run in a standalone process.

As mentioned, each of these triggers can also accept conditions. The idea of these is that they let you ignore situations where it’s pointless for you to run. You can decide whether you want to run if the user is UserPresent (meaning the device is unlocked) or UserNotPresent, if you areInternetAvailable or InternetNotAvailable, or finally if you are SessionConnected (meaning the user is logged on) or SessionDisconnected.

The conditions can help save CPU quota. For example, if you have a periodic trigger that sends information to a server, you can choose not to do that if you have no Internet available. Then you can create a trigger that runs when the Internet comes back. By skipping the first one, you’ve saved x amount of CPU quota by not calling up to the server. (Again, this is what we’ll do in our actual example.)

If you’re thinking about LOB apps, the seminal example is an operative in a truck who finishes a job at a location, hits Save, and puts the device on standby. You have two problems here: the operation may not have completed before he put the device on standby, or the device might not have had a good cellular collection at the time.

The inclination here is to use a maintenance trigger, but that trigger type has a problem in that it only runs when AC power is available. You could insist that operatives plug their device into the vehicle’s power source whenever they are travelling. (In fact, on smartphone BYOD systems, this problem normally solves itself because the battery life is so poor that operatives get into the habit of doing this themselves anyway.) Alternatively, you could hope that the operative leaves the app on the lock screen.

The upshot here is that there essentially isn’t a great way of solving this problem. In the olden days of the Windows Mobile platform, you could spin up a thread, your process would never die, and you could do what you want. On Android—a popular field-service app platform—you can create a background service that, again, runs in exactly the way that you want.

In the example we’re going to create a maintenance trigger, a time trigger, and a system trigger to detect the Internet. These will send up changes and download new work whenever they run.

Execution Model

So far in this book, we’ve skipped over the core differences between WinRT components and their libraries and .NET types and their assemblies. We’ve spoken about how there’s a shared metadata model, and we know that WinRT underpins all of the work that we do, but I’ve generally avoided going into detail about this aspect because if you’re working with .NET all of the time you generally don’t care.

When we’re using background tasks, however, we can’t avoid proactively handling the boundary between the .NET and WinRT worlds; we have to deal with it head-on. Background task components must be created as WinRT components. You can still write them in .NET, it’s just that rather than hosting them in a .NET assembly (the Class Library option in the project’s properties), you have to host them as—as it’s put in VS’s project properties windows—a Windows Runtime Component. What you get at the end is a .winmd file rather than a .dll.

This sounds fine, but there’s a problem. WinRT components have some rules that limit the richness that you can achieve with pure .NET. Compiling as a WinRT library will insist that exposed classes are sealed. There are also restrictions on overloads. These restrictions are down to restrictions in COM itself. A “hand-waving” answer as to why is that COM only supports interface inheritance, and in order to be in harmony with .NET it would also need to support implementation inheritance.

Again, this topic isn’t something I want to dwell on in this book, because unless you’re going cross-boundary (for example, creating C# components to consume in HTML/JavaScript), you don’t really need to worry about it.

When we come to our implementation, we’re going to create a separate façade library that exposes our normal .NET task handling types out in a WinRT component library. This tends to be the easiest way of squaring the circle.

Background tasks are in virtually all cases hosted in a separate executable called backgroundTaskHost.exe. (And yes—it has a lowercase b.) Any background tasks that you’re ever likely to implement will run in this way.

From an OS design perspective, running the tasks in a separate process makes sense. You can run them at lower priority, you can kill them off, and you can keep track of what’s happening in the universe of background tasks running on the device far more easily. From an app design perspective, it’s also not a bad decision. The only situation you have to deal with is not having shared state between the running app and the background task. You’ll have to deal with this aspect, but it’s not difficult.

Implementing a Sync Background Task

Now that we’ve been through the basics of how background tasks work, we’ll take a look at actually implementing one.

We’ll start by creating a task that has a MaintenanceTrigger. As mentioned previously, this needs to be hosted in a separate Windows Runtime component library. We’ll make that library depend on the UI-agnostic StreetFoo library. In the UI-agnostic library we’ll create a class that contains the logic called BackgroundSyncTask, and in the Windows Runtime component library we’ll create a façade class called BackgroundSyncTaskFacade. The easiest way to work with this stuff is to keep the Windows Runtime components as basic as possible so that you don’t hit any of the design restrictions, and work as you naturally would back in the .NET world deferring from one to the other.

We’ll also create a base class called TaskBase. When our task starts, we’ll be running in a separate background process, so we’ll need to boot up the app and log in the current user.

One of the tricky parts to get right when we register background tasks is that Windows will happily reregister the same task again and again. We have to manually go through and “reset” any task registrations that we want when we start the app.

To start, we’ll need to stub out our TaskBase class. Here’s the code:

public abstract class TaskBase

{

}

Next, we’ll build TaskHelper. This class will be responsible for registering tasks.

Task registrations are held in the BackgroundTaskRegistration class. We can walk the AllTasks dictionary looking for an existing task to cancel. Tasks are identified by name, which is arbitrary. In our case we’re going to create one task class pair (BackgroundSyncTask andBackgroundSyncTaskFacade), and then create three tasks that can trigger it. We’re going to create a MaintenanceTrigger (which will run every 15 minutes, but only on AC power), a TimeTrigger (which will run every 15 minutes, but only when we’re on the lock screen), and aSystemTrigger. This last trigger will be configured to run whenever the device goes from having no Internet connectivity to having Internet connectivity.

Our RegisterTaskAsync<T> method will take the type of the task and the name, and also provide an Action callback that will be used to configure the task. (It’s this part that will set up the trigger and the conditions.) When we come to build the task using a BackgroundTaskBuilder, it will require a value for its TaskEntryPoint property in the name of the façade class. We don’t have direct metadata access to that, so we’ll need to mangle the name ourselves. Specifically, this will be of the form StreetFoo.Client.Tasks.<Name>Facade.

Here’s the code for TaskHelper that will do both the deletion of any existing registration, and creation of a new registration. The registration method will ultimately end up being async—for now we have to fake it using Task.FromResult<bool>:

public static class TaskHelper

{

// registers a task with the given name...

public static Task RegisterTaskAsync<T>(string name, Action

<BackgroundTaskBuilder> configureCallback)

where T : TaskBase

{

// unregister any old one...

UnregisterTask(name);

// register the new one...

var builder = new BackgroundTaskBuilder();

builder.Name = name;

// entry point is StreetFoo.Client.Tasks.<Name>Facade

builder.TaskEntryPoint = string.Format("StreetFoo.Client.Tasks.

{0}Facade", typeof(T).Name);

// configure...

configureCallback(builder);

// register it...

builder.Register();

// return a dummy task...

return Task.FromResult<bool>(true);

}

// unregisters a task with the given name...

private static void UnregisterTask(string name)

{

// find it, and unregister it...

var existing = BackgroundTaskRegistration.AllTasks.Values.Where

(v => v.Name == name).FirstOrDefault();

if (existing != null)

existing.Unregister(true);

}

}

NOTE

This implementation ignores tasks that have been retired as you upgrade the app (e.g., you may have had a task in v1 that you don’t need in v2). You’ll need to unregister these too in order to be tidy, and to preserve any CPU quota that might get used up in handling the errors.

We’ll need to call RegisterTaskAsync<T> whenever we boot the app—but there’s a wrinkle. When we work like this, we have two ways in which we can boot. We can either boot interactively into the full Windows Store UI, or we can boot inside of backgroundTaskHost.exe. If we’re actually running in background mode, we don’t want to change the task registration or things will get very confusing. As the task registration only makes sense when we’re actually running the main app, my proposal is that we kick off this registration from the App class within the Windows Store app itself.

Background tasks are an area where a good logging infrastructure really pays dividends. To this end, I’m going to recommend adding the open source MetroLog library to our application. This project is loosely based on the popular .NET log4net and NLog projects, although it’s hugely slimmed down in order to be sympathetic to the reduced capabilities in the WinRT API.

To add the MetroLog library to the project, right-click on the UI-agnostic StreetFoo.Client project and select Manage NuGet Packages. Search for MetroLog, and add this project. Figure 14-1 illustrates.

Including the MetroLog Nuget package

Figure 14-1. Including the MetroLog Nuget package

There is one problem that we need to deal with, which is that we need to wait for log messages to be written before we quit; otherwise, the whole process gets torn down and we don’t have a chance to finish writing. (MetroLog’s file writing capability is asynchronous because WinRT’s file APIs are asynchronous.) We can use the ILoggerAsync interface on MetroLog to get hold of Task instances that relate to the write operations. We can collect all these and then use Task.WaitAll to flush them all through. To get an ILoggerAsync, all you have to do is cast a normalILogger instance.

NOTE

You should note, though, that as of the time of writing there is a bug in MetroLog where pending write operations that are not actively tracked are not flushed on process shutdown—thus, things that we write as we go using the normal ILogger interface may not be flushed.

This is a lot to take on board, but there’s just one more part...

In TaskBase we’ll create a RunAsync method that will accept an IBackgroundTaskInstance value. This is a WinRT interface, and an instance of this is given to us by the background task subsystem when the task runs. In the RunAsync method, we can boot the app and defer to an abstract method called DoRunAsync.

Here’s the code:

public abstract class TaskBase

{

private ILogger _logger;

// runs the operation...

public async Task RunAsync(IBackgroundTaskInstance instance)

{

// logging is a bit tricky as we have to gather all of the messages

// and flush them out...

var logTasks = new List<Task<LogWriteOperation[]>>();

// do some logging...

var asyncLogger = (ILoggerAsync)this.Logger;

logTasks.Add(asyncLogger.InfoAsync("Started background task '{0}'

(#{1})...",

instance.Task.Name, instance.Task.TaskId));

// run...

try

{

// start the app...

await StreetFooRuntime.Start("Tasks");

// defer...

await DoRunAsync(instance);

}

catch (Exception ex)

{

logTasks.Add(asyncLogger.FatalAsync(

string.Format("Background task '{0}' (#{1}) failed.",

instance.Task.Name, instance.Task.TaskId), ex));

}

// finish...

logTasks.Add(asyncLogger.InfoAsync("Finished background task '{0}'

(#{1}).",

instance.Task.Name, instance.Task.TaskId));

// wait...

await Task.WhenAll(logTasks);

}

// actual runner...

protected abstract Task DoRunAsync(IBackgroundTaskInstance instance);

// log...

protected ILogger Logger

{

get

{

if(_logger == null)

_logger = LogManagerFactory.DefaultLogManager.GetLogger

(this.GetType());

return _logger;

}

}

}

The initial implementation of BackgroundSyncTask won’t do much; it’ll just write a message to the log. Here’s the code:

public class BackgroundSyncTask : TaskBase

{

protected override Task DoRunAsync(IBackgroundTaskInstance instance)

{

this.Logger.Info("Called!");

// short-circuit...

return Task.FromResult<bool>(true);

}

}

NOTE

You can see here that we return a Task. We’ve seen this a few times now—it’s easier to build framework components as asynchronous from the outset than to retrofit them later.

By convention we’re going to create static ConfigureAsync methods in our task classes that will configure all of the triggers. In our example, we don’t need to apply conditions—if you recall, conditions are things like “only when the user is logged on.” We want all of our tasks to run regardless of state, and hence there are no conditions.

Here’s the configuration code to add to BackgroundSyncTask. This method will create three separate background tasks as we discussed previously—one triggered by a MaintenanceTrigger, one triggered by a TimeTrigger, and one SystemTrigger that will respond when any Internet connection is restored.

// Add to BackgroundSyncTask...

public static async Task ConfigureAsync()

{

// set up the maintenance task...

await TaskHelper.RegisterTaskAsync<BackgroundSyncTask>

("BackgroundSyncMaintenance", (builder) =>

{

// every 15 minutes, continuous, when on AC...

builder.SetTrigger(new MaintenanceTrigger(15, false));

});

// set up the time task...

await TaskHelper.RegisterTaskAsync<BackgroundSyncTask>

("BackgroundSyncTime",

(builder) =>

{

// every 15 minutes, continuous, when on lock screen...

builder.SetTrigger(new TimeTrigger(15, false));

});

// set up the connectivity task...

await TaskHelper.RegisterTaskAsync<BackgroundSyncTask>

("BackgroundSyncConnectivity", (builder) =>

{

// whenever we get connectivity...

builder.SetTrigger(new SystemTrigger

(SystemTriggerType.InternetAvailable, false));

});

}

You may have jumped ahead here and thought that it’s possible for all of those tasks to run at the same time. That is indeed possible! (In fact, as the MaintenanceTrigger and TimeTrigger have the same interval, if we’re on the lock screen and with AC power, these will run at the same time. Later, we’ll look at a way of stopping this from happening by handling the race condition.)

To complete building out the task, we have to construct and register the façade. When we’ve done that, we can look at debugging and running the task.

Building the Façade

To build the façade, we need to add a new project. Add a new Windows Store App→Class Library project to the solution called StreetFoo.Client.Tasks. When that’s done, open the project properties and change the “Output type” value to Windows Runtime Component. Figure 14-2 illustrates.

Setting the project to emit a Windows Runtime component library

Figure 14-2. Setting the project to emit a Windows Runtime component library

When that’s done, you’ll need to make sure that the project is building in the x86 configuration. (Recall that we did this in Chapter 3; it’s required to get the SQLite library to load properly.) Right-click on the solution, select Configuration Manager, and confirm that all projects are set to x86.Figure 14-3 illustrates.

Confirming that the projects are set to build as x86

Figure 14-3. Confirming that the projects are set to build as x86

As a final step, we need to add references. First, add a reference to the StreetFoo.Client project from StreetFoo.Client.Tasks. Second, and very important, we need the app project to have a reference to the task façade project (otherwise, it won’t get packaged). Add a reference to StreetFoo.Client.Tasks from StreetFoo.Client.UI.

Apart from the fact that it’s contained within a Windows Runtime component library, we can build the BackgroundSyncTaskFacade class normally. The only thing we have to watch out for is that we need to abide by the rules. Again, I’m not going to dig into these rules too much, but in this instance we have to make sure the class is sealed. We also have to preserve the parameter name on the Run method, which is another WinRT rule. (Specifically, here it comes through as taskInstance. You’re not allowed to rename it to instance, for example.)

The one thing we do have to handle in the façade project is a deferral. You may recall that we needed a deferral in Chapter 7 when looking at sharing, and in Chapter 8 when looking at searching. Deferrals are used in situations where Windows shells out to your app, but you need to run asynchronous methods. Because we do intend to drop into asynchronous methods to do what we need to do in this chapter, you will need to tell Windows that you’re deferring. Without a deferral, Windows will assume you’ve finished your work and tear down your process. This can’t be done in the base class, as the first await call we make will be in the façade; therefore, it has to be done in the façade.

Here’s the code, which needs to be added to the StreetFoo.Client.Tasks project:

public sealed class BackgroundSyncTaskFacade : IBackgroundTask

{

public async void Run(IBackgroundTaskInstance taskInstance)

{

var deferral = taskInstance.GetDeferral();

try

{

// defer...

var task = new BackgroundSyncTask();

await task.RunAsync(taskInstance);

}

finally

{

deferral.Complete();

}

}

}

At this point, we just need to register the task. We do this using the Declarations tab of the manifest editor. You have to do this exactly once for each façade you build, but in each case you have to tell it the sorts of triggers that you want to support. Figure 14-4 shows the task declaration. In it I’ve said that it handles system events and timer events.

Declaring the task in the manifest

Figure 14-4. Declaring the task in the manifest

You might have noticed that when we declare the task, we get an error icon on the Application tab. The error is that we need to indicate whether we support lock screen notifications. We need to set this to Badge. This means “bring the badge number or glyph” forward. We can also bring forward the text from the tile, but I’ll talk about that more when we look at the lock screen stuff (although you’d hardly ever want to bring tile text forward, as only one installed app can do this and the user would likely want to continue to use the default, which happens to be the calendar). When we indicate that we want to support lock notifications, we also need to specify an icon—specifically, the badge logo. This is a normal logo like the ones we’ve already seen, but needs to be 24×24 pixels. Figure 14-5 illustrates.

The Notifications panel on the Application tab of the manifest editor with Badge enabled

Figure 14-5. The Notifications panel on the Application tab of the manifest editor with Badge enabled

That’s all we have to do to register the task. Now we just have to try it.

Debugging the Task

Luckily for us, rather than having to wait 15 minutes for the events to trigger, we can ask Visual Studio to invoke the task for us manually.

Run the app and everything should be fine—the tasks will be registered, but you won’t see anything different. In Visual Studio, locate the Debug Location toolbar—you may need to right-click on the toolbar and add this. You’ll see a Suspend button, which you can use to manually suspend the task operations. (We’re going to talk about application lifetime in the next chapter.) This button has a drop-down component: drop it down and you’ll see the operations that you can invoke. Figure 14-6 illustrates.

Selecting a background task to invoke

Figure 14-6. Selecting a background task to invoke

Invoke one—doesn’t matter which—and your background task will run. Unless you set breakpoints, you won’t see much. However, by default MetroLog will present some trace information that you can see in the Output window:

1|2012-10-10T09:48:17.6240993+00:00|TRACE|1|Created Logger

2|2012-10-10T09:48:17.6555459+00:00|INFO|1|BackgroundSyncTask|Started background

task 'BackgroundSyncMaintenance' (#4a758aea-46c6-48ee-8243-9cc998e532cd)...

3|2012-10-10T09:48:17.7957288+00:00|INFO|4|BackgroundSyncTask|Called!

4|2012-10-10T09:48:17.7957288+00:00|INFO|4|BackgroundSyncTask|Finished

background task 'BackgroundSyncMaintenance'

(#4a758aea-46c6-48ee-8243-9cc998e532cd).

From that, we can see that the background task is working properly.

In the version of the code that you can download, I’ve configured the logging system to use a FileStreamingTarget when booted to support tasks. This logging target streams messages through to a single file. If you look in the app’s LocalState folder (see Chapter 6), you’ll find that streamed output. The reason why I bring this up is that with the tasks registered, your tasks will keep running ad infinitum, or at least until the app is uninstalled. It’s quite informative to go back and trace what happened over time. Here’s the output from my machine over a short period of time:

3|2012-10-10T10:34:44.2550430+00:00|INFO|5|BackgroundSyncTask|Started background

task 'BackgroundSyncMaintenance' (#4d7df3e2-cd8b-4306-b1b6-08e9af2dbb25)...

2|2012-10-10T10:34:44.2550430+00:00|INFO|5|BackgroundSyncTask|Started background

task 'BackgroundSyncTime' (#5d6f778e-e6d3-49e1-b8d3-dd0bcf1f4793)...

2|2012-10-10T10:34:44.2550430+00:00|INFO|6|BackgroundSyncTask|Started background

task 'BackgroundSyncTime' (#5d6f778e-e6d3-49e1-b8d3-dd0bcf1f4793)...

3|2012-10-10T10:34:44.2550430+00:00|INFO|6|BackgroundSyncTask|Started background

task 'BackgroundSyncMaintenance' (#4d7df3e2-cd8b-4306-b1b6-08e9af2dbb25)...

4|2012-10-10T10:34:44.6447323+00:00|INFO|7|BackgroundSyncTask|Called!

4|2012-10-10T10:34:44.6447323+00:00|INFO|7|BackgroundSyncTask|Called!

5|2012-10-10T10:34:44.6447323+00:00|INFO|8|BackgroundSyncTask|Called!

6|2012-10-10T10:34:44.7071181+00:00|INFO|6|BackgroundSyncTask|Finished

background task 'BackgroundSyncMaintenance'

(#4d7df3e2-cd8b-4306-b1b6-08e9af2dbb25).

7|2012-10-10T10:34:44.7227041+00:00|INFO|6|BackgroundSyncTask|Finished

background task 'BackgroundSyncTime'

(#5d6f778e-e6d3-49e1-b8d3-dd0bcf1f4793).

5|2012-10-10T10:34:44.6447323+00:00|INFO|8|BackgroundSyncTask|Called!

7|2012-10-10T10:34:44.7227041+00:00|INFO|5|BackgroundSyncTask|Finished

background task 'BackgroundSyncTime' (

#5d6f778e-e6d3-49e1-b8d3-dd0bcf1f4793).

6|2012-10-10T10:34:44.7071181+00:00|INFO|5|BackgroundSyncTask|Finished

background task 'BackgroundSyncMaintenance'

(#4d7df3e2-cd8b-4306-b1b6-08e9af2dbb25).

What this shows is that the tasks can overlap—look for the cluster of “Called!” messages in the middle of that output. As mentioned before, we don’t want this to happen because it’s just a waste of our precious CPU time. After a quick look at troubleshooting background tasks, we’ll discuss how to resolve this.

Troubleshooting Background Tasks

If things aren’t working for you, you can use the event log to do some troubleshooting. A common symptom of a broken task is when you try to break into it with Visual Studio and the entire app shuts down.

If you look in Event Viewer, you will find a whole collection of specialist event logs for working with Windows Store apps. Open Event Viewer and navigate to “Application and Services Logs”→Microsoft→Windows. If you’re struggling with anything in Windows Store apps, you’ll find diagnostic information to help at that location. If you’re struggling with background tasks specifically, look in the BackgroundTaskInfrastructure log.

You’ll find an Operational entry here. This is a straightforward informational trace on the load and run operations of the background tasks. If your task is failing, you should find references to it. (You can also turn on a diagnostic trace, but I’ve found this to be of limited use.)

Typically you will see two sorts of errors:

§ 0x80040154 will be familiar to anyone who’s worked with COM before. It means “class not registered.” The most likely reason for this happening is that you have the class name wrong, either in the manifest, or when you referenced it during task registration.

§ 0x80010008 is one that’s caused me hours of wasted time when building Windows Store apps. This is a “generic failure” message. Things to check here are a) have you changed the project to Windows Runtime component library, b) have you targeted the appropriate processor (e.g., if you’re using SQLite, you’ll need x86, not Any CPU), and c) have you referenced the task project from the Windows Store app project?

Restricting the Run Period

As we’ve mentioned a couple of times, we want to stop the tasks from overlapping. In our implementation, the MaintenanceTrigger is there really only as a backup to the TimeTrigger in case the user takes the application off of the lock screen. Likewise, there’s nothing to stop the trigger that fires when connectivity changes from happening at the same moment as the timers. What I propose here is storing an “expiration time” in the SQLite database. If a task tries to run and the expiration is not in the past, it’ll duck out early.

To finesse this, we’ll also add a check to see if we have Internet connectivity. If we don’t, we’ll duck out early and wait for notification of when connectivity has been restored.

We can check connectivity using the NetworkInformation class. You’ll either be told that you have no access, local access (i.e., LAN, but no Internet), Internet access, or constrained access. Constrained access is when you’re using a WiFi hotspot and you hit a captive portal—that is, something you have to log into (usually involvement payment) before general Internet access is enabled.

Here’s the property to add to StreetFooRuntime that will expose whether we have connectivity or not:

// Add property to StreetFooRuntime...

internal static bool HasConnectivity

{

get

{

var profile = NetworkInformation.

GetInternetConnectionProfile();

return profile.GetNetworkConnectivityLevel() ==

NetworkConnectivityLevel.InternetAccess;

}

}

Restricting the run period is tricky. As mentioned, we’ll do this by setting an expiration time in the SQLite database. When a task runs, it will look to see if this expiration time is in the past. If it is in the past, it will run. It will also work on the assumption that if it is allowed to run, it’s also responsible for setting the next expiration time.

However, if we set the next expiration time at 15 minutes, there’s a chance that we could skip an entire period if we happened to come in at the exact second that the expiration was set for. So we need a period less than 15 minutes ideally.

Windows will schedule the task intervals roughly in sync—so we know they’re run together at 15 minutes. I’m proposing setting the expiration period for five minutes. There is a slight wrinkle here in that the trigger that fires when we regain the Internet connection could collide with this expiration period too. Thus, if we detect that we have no Internet connection, we’ll reset the expiration time. The upshot of this is that we’ll always run if we are told that we have connectivity.

As a final step, we’ll also check to see if we can log the user on. We did this before when we were activated in order to handle sharing requests in Chapter 7. To do anything useful in our sync method, we need to be logged in; thus, we’ll return the result of RestorePersistentLogonAsyncas the final arbiter of whether we can run or not.

There is a problem we need to deal with now. If we have two tasks scheduled with 15-minute intervals, they will run at the same time. This is certainly something that we don’t want. What we need to do is treat this as a multithreading problem and impose a lock on the operation.

Windows will schedule each background task in its own discrete instance of backgroundTaskHost.exe. This means that we can’t rely on an in-memory synchronization primitive. What we’ll need to do in this instance is create a lock file on disk. The way that I propose doing this is to bake the capability to handle lock files into TaskBase. We’ll add a method to acquire a lock, and one to reset the lock. In fact, we’re going to cheat here—this won’t be a proper lock. A proper lock is supposed to wait until the lock is released. Our operation will be to simply abort the operation if the lock can’t be acquired. I’ve proposed doing it this way because that sort of normal locking operation is hard in WinRT. Also, it wastes the CPU quota, as our desired outcome is to escape the task as quickly as possible if it’s not appropriate to run.

The first operation is to create a lock file. We’ll create static and instance versions of this so that we can control locks from both inside the object when it’s running, and from outside the object when we’re registering it. Here’s the code—the operation is straightforward. All we need to do is try to create a file based on the name of the type that we pass in. Any failure, and we’ll assume a lock is already established:

// Add methods to TaskBase...

internal async static Task<bool> CreateLockFileAsync(Type type)

{

try

{

var filename = GetLockFileName(type);

await ApplicationData.Current.LocalFolder.

CreateFileAsync(filename,

CreationCollisionOption.FailIfExists);

return true;

}

catch

{

// any exception - just return false...

return false;

}

}

protected Task<bool> CreateLockFileAsync()

{

return CreateLockFileAsync(this.GetType());

}

To remove the lock, we’ll need some methods to do this. Here’s the code:

// Add methods to TaskBase...

internal async static Task ResetLockFileAsync(Type type)

{

try

{

var filename = GetLockFileName(type);

// get...

var file = await ApplicationData.Current.LocalFolder.

GetFileAsync(filename);

await file.DeleteAsync();

}

catch (FileNotFoundException)

{

// no-op...

}

}

protected Task ResetLockFileAsync()

{

return ResetLockFileAsync(this.GetType());

}

The reason why we have a static “reset” method is that we need to be able to smoothly recover in situations where the lock file exists on disk erroneously. (We have a static “create” method for consistency of design.) What we’ll do is when we register a task, we’ll reset the lock. This means that whenever the app is actually run, any locks are cleared. This design is a little wonky in that we could unlock at exactly the same moment a second task is trying to run, in which case we’ll run two background tasks and potentially create resource conflict issues.

Here’s the change to RegisterTaskAsync that will unlock the task. This is also where the method becomes a proper async method, too:

public static async Task RegisterTaskAsync<T>(string name, Action

<BackgroundTaskBuilder> configureCallback)

where T : TaskBase

{

// unregister any old one...

UnregisterTask(name);

// unlock it...

await TaskBase.ResetLockFileAsync(typeof(T));

// register the new one...

var builder = new BackgroundTaskBuilder();

builder.Name = name;

// entry point is StreetFoo.Client.Tasks.<Name>Facade

builder.TaskEntryPoint = string.Format("StreetFoo.Client.Tasks.

{0}Facade", typeof(T).Name);

// configure...

configureCallback(builder);

// register it...

builder.Register();

}

Now that we’ve done all that, we can go back and modify the DoRunAsync method so that it includes both the locking check and the CanRun check. Note that we have the unlock call in the finally block, and also note that we can’t await in a finally, so we’ll get a warning here.

This is also where we’ll see the expiration code for the first time. To recap, we’ll try to get the time that the task expires, abort the request if it hasn’t, and update the expiration time if it has. Finally, we’ll log on the user and use the result of that as the final answer to whether we can run.

Here’s the code (I’ve omitted the Configure method for brevity):

public class BackgroundSyncTask : TaskBase

{

private const string SyncExpirationKey = "SyncExpiration";

protected override async Task DoRunAsync(IBackgroundTaskInstance

instance)

{

// try to lock...

if (!(await CreateLockFileAsync()))

{

this.Logger.Info("Locked - skipping...");

return;

}

try

{

// should we run?

if (!(await CanRunAsync()))

return;

// log as usual...

this.Logger.Info("Called!");

}

finally

{

// reset the lock file...

ResetLockFileAsync();

}

}

private async Task<bool> CanRunAsync()

{

// do we have connectivity?

if (!(StreetFooRuntime.HasConnectivity))

{

this.Logger.Info("No connectivity - skipping...");

// clear the expiration period...

await SettingItem.SetValueAsync(SyncExpirationKey,

string.Empty);

// return...

return false;

}

// check the expiration...

var asString = await SettingItem.GetValueAsync

(SyncExpirationKey);

if (!(string.IsNullOrEmpty(asString)))

{

// parse...

var expiration = DateTime.ParseExact(asString, "o",

CultureInfo.InvariantCulture).ToUniversalTime();

// if the expiration time is in the future, do nothing...

if (expiration > DateTime.UtcNow)

{

this.Logger.Info("Not expired (expiration is '{0}') -

skipping...", expiration);

return false;

}

}

// we're ok - set the new expiration period...

var newExpiration = DateTime.UtcNow.AddMinutes(5);

await SettingItem.SetValueAsync(SyncExpirationKey,

newExpiration.ToString("o"));

// try to log the user in...

var model = new LogonPageViewModel(new NullViewModelHost());

return await model.RestorePersistentLogonAsync();

}

// code omitted...

}

You can test this by setting breakpoints and using VS to trigger the tasks manually.

NOTE

You should note in the code download that I’ve added a check to see if there is a debugger attached and skipped the expiration code. I did this to make debugging the actual syncing operation that we’ll build later easier.

Here’s some debug messages from an “organic” run of the system:

3|2012-10-12T20:14:12.7679692+00:00|INFO|5|BackgroundSyncTask|Started background

task 'BackgroundSyncTime' (#efc424e3-8d7c-405b-b1c1-c6c9d92b9871)...

2|2012-10-12T20:14:12.7629737+00:00|INFO|5|BackgroundSyncTask|Started background

task 'BackgroundSyncMaintenance' (#9bcd676b-825e-44d9-a0bb-bcfa8d6869f4)...

4|2012-10-12T20:14:13.2420173+00:00|INFO|8|BackgroundSyncTask|

Locked - skipping...

5|2012-10-12T20:14:13.2720318+00:00|INFO|8|BackgroundSyncTask|

Finished background task 'BackgroundSyncMaintenance'

(#9bcd676b-825e-44d9-a0bb-bcfa8d6869f4).

7|2012-10-12T20:14:13.3970527+00:00|INFO|6|BackgroundSyncTask|Called!

8|2012-10-12T20:14:13.8802410+00:00|INFO|5|BackgroundSyncTask|Finished

background task 'BackgroundSyncTime'

(#efc424e3-8d7c-405b-b1c1-c6c9d92b9871).

Implementing the Sync Function

Now that we have the infrastructure in place, we can look at implementing the actual sync function.

This function will have two operations. First, it will send up to the server any new data that is waiting to go. If you’re building field-service apps, it’s important that this happens in the background. Oftentimes, work is actually signed off in poor signal areas. You then want to take advantage of the device traveling around to the next job to increase your chances of getting a working connection.

Second, the device needs to pull down new work to do. Again, in field service this is important, as you don’t want the operatives idle, or indeed doing canceled work. Keeping the pending work list fresh is vital.

Coming back to the discussion at the start of this chapter, 15 minutes is probably too long to wait for either of these operations. If you have new work to do, my recommendation is to send a push notification to the device. This will prompt the user to open the app, whereupon you can explicitly go out because at that point the app will be running properly, and you can use a foreground activity to get the work to do from the server. This is, in my opinion, an acceptable compromise.

You may also recall that at the start of this discussion I said that to preserve CPU cycles we’d grab new work from the server, but not actually process it into the database. We’ll save that new work to disk and process it when the app is back in the foreground.

Sending Changes

We’ll look first at how to send up changes. We laid the foundation for this work back in Chapter 11, when we implemented the report singleton view. We added a Status property to ReportItem that would track whether an item had to be sent to the server. We also created anImageChanged property that would track whether the image had to be transmitted back up to the server.

We will now see how to handle sending up changes to the data, but in these pages I’m not going to go through uploading images. I’ve chosen not to make the server handle user images, for various and obvious reasons.

NOTE

You can transmit images using the HttpClient class that we have seen variously used in previous chapters.

The first thing we’ll need is a proxy to call up to the server with the changes. Actually, we’ll need two proxies, as the insert and update operations are separate. Specifically, we’ll need to call HandleCreateReport and HandleUpdateReport. To keep things simple, we’ll just look at inserts in these pages—the code download does support updates.

If you recall, each ReportItem has a local ID (Id) and a server-side ID (NativeId). In Chapter 11, when we added the ability to insert new reports into the local database, we set NativeId to be a GUID. At this point, when we upload the report to the server, we’ll get back the newly allocated server-side ID. We’ll have to patch this new value into the NativeId field of the local SQLite database after the insert operation completes.

To facilitate this, we’ll create a CreateReportResult class. It’s been a while since we built service proxies, but if you recall we have a convention whereby any responses from the server are wrapped in specialist “result objects” that extend ErrorBucket. We’ll either get an error back from the server, or we’ll get a native ID. Here’s the implementation of CreateReportResult:

public class CreateReportResult : ErrorBucket

{

public string NativeId { get; private set; }

internal CreateReportResult(string nativeId)

{

this.NativeId = nativeId;

}

internal CreateReportResult(ErrorBucket bucket)

: base(bucket)

{

}

}

The next step is to create the interface for the service proxy. The CreateReport method will take primitives that map to the fields in the ReportItem class. Here’s the code:

public interface ICreateReportServiceProxy

{

Task<CreateReportResult> CreateReportAsync(string title,

string description,

decimal longitude, decimal latitude);

}

Now we can look at the implementation. We’ve built a few service proxies now and they generally look the same, so I’ll just quickly present this one. Please refer back to previous chapters if the construction is unclear:

public class CreateReportServiceProxy : ServiceProxy,

ICreateReportServiceProxy

{

public CreateReportServiceProxy()

: base("CreateReport")

{

}

public async Task<CreateReportResult> CreateReportAsync(string title,

string description, decimal longitude, decimal latitude)

{

// package up the request...

var input = new JsonObject();

input.Add("title", title);

input.Add("description", description);

input.Add("longitude", longitude.ToString());

input.Add("latitude", latitude.ToString());

// call...

var executeResult = await this.Execute(input);

// get the user ID from the server result...

if (!(executeResult.HasErrors))

{

var reportId = executeResult.Output.GetNamedString("reportId");

return new CreateReportResult(reportId);

}

else

return new CreateReportResult(executeResult);

}

}

Note how we’re extracting the new server-side report ID out of the result. We’ll use that later.

For the actual sync, we’ll build the functionality for doing this in the ReportItem class and defer out to that from the BackgroundSyncTask class.

As mentioned, there will be two phases to this: we’ll send changes first, and then we’ll download new work. For the sending phase, we’ll need to get the reports that have changed, and then decide which service proxy to call.

NOTE

This implementation has simplified error handling, compared to what you would need in production apps. In production scenarios, it’s essential that you do not lose data that exists only on the device. This requires elegant error handling as well as complex and well-tested retry algorithms. In this implementation I’ve proposed being rather blunt—if you can’t send the change, junk it. This really isn’t good enough for production, but it’s OK for our illustration. By the way, the hardest part of this problem is having sketchy connectivity (i.e., connectivity that comes and goes inconsistently) during the sync process. You have to handle each call carefully, trapping and handling errors and retrying appropriately. This is notoriously hard to test in the lab.

We need to handle the sending portion with multiple methods. We’ll create a static database query method that will return all of the changed jobs. We’ll create an instance method in ReportItem that will actually send the changes for us. We’ll round this off with a static method that will coordinate all of that.

Here’s the implementation of GetLocallyChangedReportsAsync:

// Add method to ReportItem...

private static async Task<IEnumerable<ReportItem>>

GetLocallyChangedReportsAsync()

{

var conn = StreetFooRuntime.GetUserDatabase();

return await conn.Table<ReportItem>().Where(v => v.Status !=

ReportItemStatus.Unchanged || v.ImageChanged).ToListAsync();

}

Next, the method that glues it all together. This will call out to get the changes and then defer to the individual per-report update method. I’ve added some logging in here so that you can get a better idea of what’s happening. Here’s the code:

// Add method to ReportItem...

internal static async Task PushServerUpdatesAsync()

{

var logger = LogManagerFactory.DefaultLogManager.GetLogger

<ReportItem>();

logger.Info("Pushing server updates...");

// get all of the changed reports...

var reports = await GetLocallyChangedReportsAsync();

// how many?

logger.Info("Found '{0}' changed report(s)...", reports.Count());

// if nothing, quit...

if (!(reports.Any()))

return;

// otherwise...

var tasks = new List<Task>();

foreach (var report in reports)

tasks.Add(report.PushServerUpdateAsync());

// wait...

await Task.WhenAll(tasks);

// finished...

logger.Info("Finished pushing updates.");

}

NOTE

I haven’t implemented this next recommendation for fear of making the code fussy, but a trick you can do with this sort of code is continually checking that you have connectivity before you go through each step.

For example, you might call out to get the local reports, but abort the operation if it turns out that you have no connection before you go out to PushServerUpdateAsync. This will reduce your CPU quota—or more rightly you can save it to such time as you know you have better connectivity.

Finally, we can look at the transmission portion. This is relatively easy—we examine the Status property and then branch accordingly. Here’s the code:

// Add method to ReportItem...

internal async Task PushServerUpdateAsync()

{

this.Logger.Info("Pushing update for #{0} ({1})...", this.Id,

this.Status);

// what happened?

if (this.Status == ReportItemStatus.Unchanged)

{

// no-op...

}

else if (this.Status == ReportItemStatus.New)

{

// insert...

var service = new CreateReportServiceProxy();

var result = await service.CreateReportAsync(this.Title,

this.Description, this.Longitude, this.Latitude);

// patch back the native ID, if it worked...

if (!(result.HasErrors))

this.NativeId = result.NativeId;

else

this.Logger.Warn("Failed to insert report: " +

result.GetErrorsAsString());

}

else

throw new NotSupportedException(string.Format("Cannot handle '{0}'.",

this.Status));

// reset our flag...

this.Status = ReportItemStatus.Unchanged;

// set...

var conn = StreetFooRuntime.GetUserDatabase();

await conn.UpdateAsync(this);

}

NOTE

This implementation only handles inserts. The code download handles updates and deletes as well.

Notice in the code how we reset the flag when we’re done—this is the part that sets the Status property back to Unchanged. You can also see that we patch back the server-side report ID into the local copy, replacing our temporary ID.

To test this, run the app, log on, and create a new report. To upload it to the server, kick off the background task using Visual Studio. You’ll be able to see what’s happening using the Output window. If you really want to prove to yourself that the change has occurred, add a new report, sync, shut down the app, and uninstall the app. Deploy and run the app again, and when you’ve logged in and the reports have been downloaded from the server, the report that you added will come back again. Note that you’ll get a stock image because we don’t transmit any image that you’ve taken up to the server.

Another thing you can do is to create a new report and leave the app idle until the background task runs naturally. Or, you can create a new report—turn off your machine’s network connection and then turn it back on. This will kick off the system connectivity trigger, and you’ll see the update.

Receiving New Work

Now that we know that we can trigger a task and send updates, we’ll look at how we can adapt this to receive new work.

There is some subtlety here. I’m going to propose that we do this in quite a blunt fashion by downloading the entire set of reports from the server each time. In a production app, a better way to do this is to download a list of report IDs with version numbers. You can then check to see if you have new or changed reports and download them piecemeal. This saves both bandwidth and battery, and is a good thing to do. However, it makes the implementation quite complicated—more complicated than I would want it in this book.

Also, there is a problem in that our background task will always run whether the app is running or not. If new work is available and the app is running in the foreground, it would be ideal to signal the running app so that it can update the UI to indicate that new data is available. This is actually quite hard to do, and I’ll talk more about that in the next section.

For now, we’ll deal with the download. As we’ve discussed previously, to save our CPU quota we’ll spool the report data to disk and pick it up again when we need it.

To start, we’ll modify BackgroundSyncTask so that after it uploads any changes, it’ll download the reports. We’ll then use JSON.NET to stringify the reports that we download and store them in TempState in a file called SpooledReports.json. Here’s the code:

public class BackgroundSyncTask : TaskBase

{

private const string SyncExpirationKey = "SyncExpiration";

internal const string SpoolFilename = "SpooledReports.json";

protected override async Task DoRunAsync(IBackgroundTaskInstance

instance)

{

// should we run?

if (!(await CanRunAsync()))

return;

// send up changes...

await ReportItem.PushServerUpdatesAsync();

// still have connectivity?

if (StreetFooRuntime.HasConnectivity)

{

this.Logger.Info("Getting reports from server...");

// get...

var proxy = ServiceProxyFactory.Current.GetHandler

<IGetReportsByUserServiceProxy>();

var reports = await proxy.GetReportsByUserAsync();

// errors?

if(!(reports.HasErrors))

{

this.Logger.Info("Stashing reports on disk...");

// save...

var json = JsonConvert.SerializeObject(reports.Reports);

var file = await ApplicationData.Current.TemporaryFolder.

CreateFileAsync(SpoolFilename, CreationCollisionOption.ReplaceExisting);

await FileIO.WriteTextAsync(file, json);

}

}

}

// code omitted...

}

You’ll note that I’ve added a check for connectivity (i.e., the call to StreetFooRuntime.HasConnectivity). Again, this was something mentioned before; it’s an easy check to do, and it quickly allows us to see if we’re likely to be successful with the call and preserve our CPU quota if not.

Next we have to load up that spooled data. We have a method in ReportItem called UpdateCacheFromServerAsync. This method calls up to the server and updates the local database. My proposal is that if we have spooled data on disk, we short-circuit this operation and rather than go out to the network, we’ll just parse the spooled JSON containing the reports.

NOTE

I must admit, I wondered about whether this was a good approach for a long time. It seems “impure” to work like this—we’d be hijacking an expected operation and doing something non-obvious and tricky to trace. However, in the end I got to a point where I felt it was pragmatic enough to use here, and also a decent example to show you.

The first thing we’ll need is a method that loads up the spooled reports from disk. Here’s the code:

// Add method to ReportItem...

private static async Task<IEnumerable<ReportItem>>

GetSpooledReportsAsync()

{

IStorageFile file = null;

try

{

file = await ApplicationData.Current.TemporaryFolder.

GetFileAsync(BackgroundSyncTask.SpoolFil

ename);

}

catch (FileNotFoundException)

{

return null;

}

// load...

try

{

var json = await FileIO.ReadTextAsync(file);

return JsonConvert.DeserializeObject<IEnumerable<ReportItem>>

(json);

}

finally

{

// delete the file—we have to do this regardless, but we can't

// wait here...

file.DeleteAsync();

}

}

You’ll notice here that we delete the file when we’re done. There’s a wrinkle here where we can’t await in a finally, so we’re just firing and forgetting the delete. (We saw the same thing when removing the lock file in TaskBase.) This could create a race condition where we collide the file. (Even though our app and the background tasks are running in separate isolated processes, the file effectively becomes shared memory and hence needs to be synchronized for multithread/multiprocess access.) This would lead us to the problems with locking with async—something that’s beyond the scope of this chapter. If you use the normal locking routines (including the lock keyword), bad things will happen. There are ways that you can do this. Stephen Toub, a member of the Microsoft team that works on the asynchrony implementation, has a number of blog posts on this topic, including “Building Async Coordination Primitives,” of which AsyncLock is one example. My hope is that these primitives will end up in the full WinRT library over time.

But I digress: the trick is to delete the file so that this is a one-shot deal, as opposed to stopping the app from ever calling back to the server to get fresh data.

While we’re here, we’ll also need a method that indicates whether we have a file available on disk. Here’s the code:

// Add to ReportItem...

internal static async Task<bool> HasSpooledReportsAsync()

{

try

{

await ApplicationData.Current.TemporaryFolder.GetFileAsync(

BackgroundSyncTask.SpoolFilename);

return true;

}

catch (FileNotFoundException)

{

return false;

}

}

We can now modify the code that updates the cache to use the spooled file. Here it is:

// Modify method in ReportItem...

public static async Task UpdateCacheFromServerAsync()

{

IEnumerable<ReportItem> reports = await GetSpooledReportsAsync();

if (reports == null)

{

// create a service proxy to call up to the server...

var proxy = ServiceProxyFactory.Current.GetHandler

<IGetReportsByUserServiceProxy>();

var result = await proxy.GetReportsByUserAsync();

// did it actually work?

result.AssertNoErrors();

// set...

reports = result.Reports;

}

// update...

var conn = StreetFooRuntime.GetUserDatabase();

foreach (var report in reports)

{

// load the existing one, deleting it if we find it...

var existing = await conn.Table<ReportItem>().Where(v =>

v.NativeId == report.NativeId).FirstOrDefaultAsync();

if (existing != null)

await conn.DeleteAsync(existing);

// create...

await conn.InsertAsync(report);

}

}

This will almost work end to end. The problem we have to fix now is that although this will run fine when we’re in the background, when we navigate to the Reports page it’ll use the data in SQLite (as the behavior we’ve had thus far is that unless we explicitly request a refresh, we’ll “go local” and get the current version in SQLite).

This is why we needed the method to see if spooled reports were available. If we detect that they are, we can force a refresh whenever the Reports page is activated. This will flush through the changes at a point in time when it’s most appropriate, and when we’re in the foreground and hence not using up our CPU quota. Here’s the code:

// Modify method in ReportsPageViewModel...

public override async void Activated(object args)

{

base.Activated(args);

// do we have spooled reports?

var force = false;

if (await ReportItem.HasSpooledReportsAsync())

force = true;

// refresh...

await DoRefresh(force);

}

To test this, locate the app’s TempState folder in File Explorer and you’ll find there is no SpooledReports.json. Run the project and use Visual Studio to kick off the background operation. This will create the file; immediately terminate the app using Debug→Stop Debugging.

Start the app again, and when the Reports page displays you’ll find the file has disappeared. The badge will also be updated on the app tile, although you may not notice that if you haven’t added new reports. For extra points, you can create a report and then repeat the process. If you do this, you’ll see the badge value change.

Signaling the App from the Background Task

There’s a gaping hole in the background task APIs—namely, that there’s no elegant way to signal information from background tasks to any running tasks. As mentioned, if the Reports page is active and the background task triggers and discovers new jobs, it should be able to tell the Reports page to update itself. There is no standard mechanism in the APIs to do this. There is a mechanism for passing back numeric information to drive a progress bar, but that’s it.

The documentation says that you’re supposed to use persistent storage to do this, the implication being that you store a file on disk on one side, and detect it and respond on the other. Curiously, this is exactly what we’ve done with our SpooledReports.json—we create it in the background task and then ReportPageViewModel modifies its behavior because of its existence.

When I was prototyping the work for this chapter, I created a signaling system based on the “observer” design pattern. I haven’t put it in these pages because of space constraints. The general shape of the solution looked like the following. You may care to build out your own implementation along these lines:

1. Create a base class called SignalBase. The idea is that developers would create specializations of these (e.g., NewReportsAvailableSignal).

2. When view-models start, they tell a manager class that they want to be told about signals. (This is the central part of the “observer” pattern.) View-models also implement an interface called ISignalSink if they want to participate in this activity.

3. Next, create a database entity called SignalItem. When background tasks want to send a signal, they create instances of SignalItem and store them in SQLite. The approach here is that each specialized signal class can contain read/write properties that were JSON stringified into the SQLite database along with their owning class. Effectively what happens here is that the specialized signals end up being serialized into the database.

4. The manager creates a recurring ThreadPoolTimer. Every n seconds (I did 15), it will load up any SignalItem instances from SQLite, and then clear the table.

5. Each SignalItem is then examined in turn and a specialized signal instance created from it, along with any serialized data. The view-models that registered an interest in this are contacted in turn and passed the signal via the ISignalSink interface.

When I tried this, it worked very well. The background task would run and the app would automatically update itself. As mentioned, I didn’t include it only because of space constraints.

Putting the App on the Lock Screen

Finally, we need to put the app on the lock screen. We do this by calling the RequestLockScreenAsync method on the TaskHelper WinRT class. You can’t check to see if you are on the lock screen; otherwise, you could keep nagging the user to add you, and that’s the sort of behavior that Microsoft is not keen on from well-behaved apps. Here’s the code (I’ve omitted code for brevity):

// Modify method in App...

protected override async void OnLaunched(LaunchActivatedEventArgs args)

{

// Do not repeat app initialization when already running, just

ensure that

// the window is active

if (args.PreviousExecutionState == ApplicationExecutionState.

Running)

{

Window.Current.Activate();

return;

}

// code omitted...

// configure tasks...

await BackgroundSyncTask.ConfigureAsync();

// ask about the lock screen...

await TaskHelper.RequestLockScreenAsync();

}

If you run the code now, you’ll be prompted to add the app to the lock screen. Figure 14-7 illustrates.

Prompting for permission to add to the lock screen

Figure 14-7. Prompting for permission to add to the lock screen

The app won’t appear on the lock screen until the badge has changed. (You’re supposed to show new notifications here, so any old badge value won’t carry forward.) You can change the badge number by refreshing the Reports page. Once you add the app to the lock screen and refresh the reports, you’ll see something like Figure 14-8.

The lock screen with the StreetFoo “pika” icon and badge

Figure 14-8. The lock screen with the StreetFoo “pika” icon and badge

Now you’ll find that your TimeTrigger tasks will also run. You’ll also get more CPU quota, but that’s difficult to measure!