Resilient background tasks in Windows and Windows Phone

(The code for this post is available on GitHub. The app has been uploaded to the Windows store and the Windows Phone store).

Background tasks are a great way to keep your Windows 8.1 or Windows Phone application up-to-date with the latest data or information. They're also useful for updating your app's live tiles, updating a badge on the lock screen or showing alert notifications.

However, setting up a background task can be a little tricky — there are a bunch of gotchas that you'll need to know about; if you don't, you'll wind up with some pretty frustrating behavior on your hands.

In this post, I'll show you how I've handled all of these little gotchas and built an app with a bit more resilience in its background tasks.

(NB: If you're an advanced developer — you know how to build a Windows 8 or Windows Phone app and background tasks are a trivial thing for you — you might want to skip ahead to setting up the background task toward the second half of this post. This tutorial is probably going to be a little slow for you — it's verbose and meant to help developers who are new to the platform get started with background tasks.)

The scenario: Let's say we're building a Windows and Windows Phone app to follow a certain channel on YouTube. In this case, we'll be following Jesse Cox channel. We want our app to continuously scan Jesse's channel, and then show a notification when he uploads a new video.

I won't go into the YouTube API in this post, but luckily we don't need to know too much about it. We can scan a channel for new videos by polling the https://gdata.youtube.com/feeds/api/users/CHANNELNAME/uploads?v=2&alt=jsonc URL scheme. The channel we'll need to poll is OmfgCata.

To get started, open up Visual Studio on your Windows 8.1 machine and create a new, blank Universal App (File => New => Project => Templates => Visual C# => Store Apps and select Blank App (Universal Apps)). I've named mine 'Space Butterfly`, but you can name yours whatever you want; just remember to substitute the 'Space_Butterfly' namespace with your own whenever you see it.

Setting up the project

Visual Studio will create a new solution with three different projects in it: "Space Butterfly.Windows", "Space Butterfly.WindowsPhone" and "Space Butterfly.Shared".

We'll need to do a quick bit of maintenance before we build our background tasks. First, we need to get a few common classes that will help with navigation, app suspension and so on. I haven't yet found a NuGet package that will do this for us, but luckily Visual Studio will automatically download the classes for us when you create a new basic XAML page.

Right-click on your Shared project, New Item => Basic Page. Visual Studio will download everything we need to a folder named Common in the shared project. As soon as it's done, go ahead and delete the page you just created.

(If you'd rather add these classes yourself, you can find all four files on the GitHub repository. Again, remember to replace the Space_Butterfly namespace with your own.)

Next, we're going to create something that I like to call a "Core". It's a helper project that will be shared across multiple projects in our solution; we'll use it to poll the YouTube API and deserialize the JSON it returns.

You might be wondering, "Why do we need to create a whole new project? If it's shared code, why not just put it in the shared project"? There's only one reason it's not going in the shared project: background tasks must be placed in their own project and then referenced by the app projects. However, two projects cannot reference each other; e.g. if Project A references Project B, then Project B cannot reference Project A.

If we want to keep things DRY, we'll need to put the core in it's own project which can be referenced by both the background task project and the app projects.

Let's create those two other projects now: the Background Tasks project and the Core project. In Visual Studio's Solution Explorer, right-click on the solution itself, Add => New Project => Visual C# => Store Apps and then select Class Library (Portable for Universal App). We'll name the first one 'Background Tasks'. Once finished, do the same thing to create a second project named 'ButterflyCore'.

Creating the extra projects

Let's get the project references set up. First, right-click on your Windows project, Add => Reference => Projects, then check both 'Background Tasks' and 'ButterflyCore' (or whatever you've named your projects). Do the same for your Windows Phone project.

After that, right-click on your 'Background Tasks' project, Add => Reference => Projects, then check 'ButterflyCore'.

Adding project references

Let's fill out the Core project before we get into the background tasks. Right-click in your Core project, Add => New Folder named 'Models'. Inside that new folder, right-click, Add => New Item => Class named 'YouTube.cs'. Here's the code for that new class:

We're going to be deserializing the YouTube API's JSON response into this YouTube class, which we'll get to in just a second. After creating the YouTube.cs class, we'll create another new class inside the 'Models' folder named 'CommonWebResponse'. Here's the code:

The CommonResponse and CommonWebResponse classes are just simple generic objects that will tell us if querying the YouTube API was successful, or if it encountered an error.

Next up, we need to write a small method that will query the YouTube API and deserialize the returned JSON into an instance of the class that we just made. Make a new class in the Core project named 'Core.cs'. Once again, here's the code for the new class:

###The background timer task

It's time to go ham with our background timer task. Add a new class named 'SourceCheckerTask.cs' to your Background Tasks project; this will be the only class in the project, and a relatively simple one at that. Opening up the new class file should give you something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Background_Tasks
{
    class SourceCheckerTask
    {
    }
}

To turn this class into a background task, we'll need to make it public and sealed, add a few more using statements, and then tell the compiler that it implements the Windows Runtime's IBackgroundTask interface:

...
using Windows.Storage;
using Windows.ApplicationModel.Background;
using ButterflyCore;
using Windows.UI.Notifications;
using Windows.Data.Xml.Dom;

namespace Background_Tasks
{
	public sealed class SourceCheckerTask: IBackgroundTask
	{

	}
}
...

Visual Studio's intellisense will probably give you the infamous red squiggly line under 'IBackgroundTask'. Mousing over that line will tell you that your new class does not implement IBackgroundTask.Run(IBackgroundTaskInstance). Click on the red squiggly line and pressing CTRL + ., then click on 'Implement interface'.

Visual Studio will automatically create a method in the class called Run. Our ButterflyCore uses asynchronous web requests, though, so we need to make the background task async. However, making a background task asynchronous isn't quite as simple as usual; we can't just slap async onto the method signature and call it good.

Instead, we need to use the task instance's deferral to specifically tell the task that it should wait until our async code is complete:

...
public async void Run(IBackgroundTaskInstance taskInstance)
{
	//Use .GetDeferral to prevent race conditions where the task finishes before async code completes.
	var deferral = taskInstance.GetDeferral();

	// TODO: Write the code that we want to run in the background here.

	//Complete the task after all async code is finished.
	deferral.Complete();
}
...

Let's finish out the background task now by polling the YouTube API and showing a toast if there's been a new video:

...
//Use .GetDeferral to prevent race conditions where the task finishes before async code completes.
var deferral = taskInstance.GetDeferral();

var core = new Client();
var localSettings = ApplicationData.Current.LocalSettings;

//Query the YouTube API for the latest video
var query = await core.GetYouTubeUploads(1);

//Ensure it was successful and that it contains data
if (query.Success && query.Data?.data?.items != null)
{
	//Get id of last checked video from app's local settings
	var lastVideoId = localSettings.Values["LastVideoId"] as string;

	//Get the latest video from the query
	var latestVideo = query.Data.data.items.FirstOrDefault();

	//If lastVideoId and latestVideo.id do not match, the returned video is new
	if(lastVideoId?.ToLower() != latestVideo?.id?.ToLower())
	{
		//Show notification
		try
		{
			var xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText01);

			//Set toast image
			((XmlElement)xml.GetElementsByTagName("image")[0]).SetAttribute("src", latestVideo.thumbnail.sqDefault);

			//Set toast text
			xml.GetElementsByTagName("text")[0].AppendChild(xml.CreateTextNode("Jesse Cox has uploaded a new video!"));

			//Show toast
			ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(xml));
		}
		catch (Exception e)
		{
			//Do nothing. No way to warn user
		}

		//Store new video's id
		localSettings.Values["LastVideoId"] = latestVideo.id;
	}
}

//Complete the task after all async code is finished.
deferral.Complete();
...

(NB: This example is using the new C# null coalescing language feature, which currently requires Visual Studio 2015 ultimate. The if(lastVideoId?.ToLower() != latestVideo?.id?.ToLower()) check is an example of null coalescing.)

Each video returned by YouTube has a unique Id; comparing that Id to the Id of the last video that we stored will tell us if this video is new or not. If it's new, we show a toast notification.

Here's the final code for the 'SourceChecker.cs' class:

###The app's MainPage

I'm a big fan of registering my background tasks within an actual XAML page rather than immediately after the task starts in the App.cs file. That's because in a Windows 8 app, you need to request background access and lock screen access to run a background task on a timer. When that request is made, a dialog will be shown to the user asking them if they want to grant access or not.

If you request access from the App.cs file, your main entry page will not load and the app will appear blank; the user has no idea what your app even looks like and you're already asking for permissions. However, if you ask for permission from a page, it will actually load and be visible behind the dialog.

Our app will need a main page anyway, so let's bang that out quick. Create a new Basic Page file in each project (Windows and Windows Phone) with Right-click => Add New Item => Basic Page, named MainPage.xaml.

Even though this a universal app project, the code in the Windows project's MainPage.xaml file is going to look a little bit different from the Windows Phone project's MainPage.xaml; they have radically different screen-sizes which calls for different layouts.

Here's the code for the Windows project's MainPage.xaml:

And the code for the Windows Phone project's MainPage.xaml:

(NB: Make sure you change the namespaces in your xaml files to match whatever namespace you chose for your project.)

Let's quickly integrate these two pages with our ButterflyCore, then we can move on to registering the background tasks and making them more resilient. Open up each MainPage.xaml.cs file — if you don't see it, click the down-arrow next to the MainPage.xaml file in Visual Studio's solution explorer. Add the following code to both of the files:

I won't spend a whole chunk of time explaining what's going on in these xaml pages that we just made because this post is about setting up background tasks — not designing and writing xaml pages. Suffice it to say that the MainPage will show the ten latest videos from our YouTube channel.

If you build and launch the app right now, you should get something that looks a little bit like this:

Screenshot of the app

###Registering the background task and making it resilient

Let's go hard in the paint to get our background task registered. Using background timer tasks, or any background task, requires that you declare background task capability in your app's manifest. You'll need to open up the package.appxmanifest files in both your Windows project and your Windows Phone project.

With your manifest open, go to the Declarations tab, select 'Background Tasks' from the Available Declarations dropdown and then press Add. Check the 'Timer' task under the Properties list that appears, then enter the namespace and class of the background task you created. In this example, it would be 'Background_Tasks.SourceCheckerTask'.

Declaring background task capability in package.appxmanifest

If you haven't done it yet, you'll probably get a warning after declaring background tasks telling you that you need to supply badge images for your app. Just click the Visual Assets tab and fix the errors there by supplying some images. To save some time, you can get the image's that I'm using in this project here (Windows) and here (Windows Phone).

We have three different situations that we'll need to keep in mind when we register our background task:

  1. On Windows Phone, background tasks must be removed and re-registered whenever the app is updated (typically through he Windows App Store).
  2. On Windows 8, apps must specifically ask for — and receive — permission directly from the user to both run in the background and be placed on the lock screen. If the user denies permission, the app cannot run background tasks.
  3. While Windows Phone does not prompt the user for permission to run in the background, the built-in battery saver app will decide if your app can run in the background. If the user has too many apps that use background tasks, your app will automatically be denied permission to run in the background.

Let's start registering our task to run — I'll explain what we'll do to handle the above situations as we go along. In your shared project, create a new class called TaskHelper.cs; make it internal and set a readonly string for the name of your task:

namespace Space_Butterfly
{
	internal class TaskHelper
	{
		private readonly string TaskName = "SourceCheckingTask";
	}
}

(NB: This string does not have to match the actual name of your task class. It's just used to make a registered task human-readable.)

We're going to have two methods inside of this TaskHelper class. The first method, called CheckAppVersion, will check if the app has been updated since the last time it ran. According to Microsoft's background task guidelines, our Windows Phone app must remove background access, request it again and then re-register background tasks after an app update.

With those things in mind, this CheckAppVersion method will return true if the app has been updated (and thereby background access has been removed), and false if not.

public bool CheckAppVersion()
{
	string appVersion = string.Format("{0}.{1}.{2}.{3}",
                    Package.Current.Id.Version.Build,
                    Package.Current.Id.Version.Major,
                    Package.Current.Id.Version.Minor,
                    Package.Current.Id.Version.Revision);

	if (ApplicationData.Current.LocalSettings.Values["AppVersion"] as string != appVersion)
	{
		// Our app has been updated
		ApplicationData.Current.LocalSettings.Values["AppVersion"] = appVersion;

		// Call RemoveAccess
		BackgroundExecutionManager.RemoveAccess();

		return true;
	}
	else
	{
		return false;
	}
}

This is only necessary on Windows Phone, so we'll need to make sure that we don't call this method later on from the Windows app.

Microsoft's guidelines for background tasks recommends checking to see if your task is registered by using the BackgroundTaskRegistration.AllTasks list. However, what the guide does not tell you is that your task can exist in this list even if it's not registered or allowed to run. This situation can arise when your app has been updated and background access has been removed (as it should be on Windows Phone).

The second method in our TaskHelper.cs class, called RegisterTasks, will handle that situation. It takes a boolean parameter that will force the task to register, even if it was previously registered before. That way we can be sure that our task gets registered when it should, even if it's already in the list of tasks:

public void RegisterTasks(bool forceRegister = false)
{
  	//Try to find task
  	var task = BackgroundTaskRegistration.AllTasks.FirstOrDefault(x => x.Value?.Name == TaskName);

   	if (task.Value == null || forceRegister)
  	{
     	if (forceRegister)
      	{
         	//Unregister last task.
          	task.Value?.Unregister(true);
      	}

       	//Register task
      	var taskBuilder = new BackgroundTaskBuilder()
       	{
       		Name = TaskName,
         	TaskEntryPoint = "Background_Tasks.SourceCheckerTask"
      	};

		//Run this task every 30 minutes as long as there is an internet connection.
		taskBuilder.SetTrigger(new TimeTrigger(30, false));
        taskBuilder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));

    	var registration = taskBuilder.Register();
	}
}

With the TaskHelper built, let's switch over to the MainPage.xaml.cs file in your Windows Phone project.

One important thing to keep in mind when registering background tasks is that you must request background access from the system by calling BackgroundExecutionManager.RequestAccessAsync(). If the user is on Windows 8, they'll get a prompt asking them to give the app permission to run in the background and place it on the lockscreen. If they deny either of these permissions, your app will not be allowed to run background tasks.

We're going to check if the app has been updated — which will automatically remove background access as required for a Windows Phone app) — and then request background access. We'll do both of these things inside the navigationHelper_LoadState method. That way, the app will already be loaded in the background by the time we ask for background access.

...
private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
   	LoadLatestVideos();

  	var taskHelper = new TaskHelper();
 	string backgroundAccessStorageKey = "LastBackgroundAccessStatus";

	//App must revoke background access, then reregister tasks if it was updated
  	var appUpdated = taskHelper.CheckAppVersion();

   	//Check/Request background access
  	var request = await BackgroundExecutionManager.RequestAccessAsync();

  	if (request.ToString() == "Denied")
 	{
  		await new MessageDialog("Your phone has disabled background tasks for this app. Without background tasks, the app cannot notify you of new videos or current Twitch streams. Please enable background access for this application by going to Settings => Battery Saver => Usage => Jesse Cox for Windows.", "Background Access").ShowAsync();
   	}
   	else
  	{
       	//Force register a new task if last background access check was not granted
      	var forceRegister = localSettings.Values[backgroundAccessStorageKey] as string != request.ToString() || appUpdated;

       	taskHelper.RegisterTasks(forceRegister);
  	}

   	//Save the new background access status
	localSettings.Values[backgroundAccessStorageKey] = request.ToString();
}
...

Notice when we request background access that we're able to check the answer by calling request.ToString(). On Windows 8 the answer will be decided by the user when they choose to grant your app background access and place it on the lock screen. However, on Windows Phone the answer is going to be decided by the OS rather than the user. A couple of things go into that decision, but the biggest factor is how many of the user's apps are running in the background already.

The worst part? There's nothing you can do about it. I've had brand new apps released on the Windows Phone store that were immediately denied background access because of battery concerns.

request.ToString() will always be 'Denied' when the app is not granted background access. At that point we'll show a new MessageDialog which informs the user that the app has no background access and how they can fix it.

A screenshot of the access denied dialog

But what happens if background access is granted instead? If that's the case, the first thing we do is determine if we need to force register the new task. We can do that by checking the phone's localSettings to determine if the app had background access the last time it ran. You'll note that we aren't pulling that result out of nowhere though — we're storing the RequestBackgroundAccess result in localSettings right before the navigationHelper_LoadState method finishes.

We'll take that forceRegister boolean and pass it to the TaskHelper that we recently created. The helper will determine if we need to create the task for the first time, if it needs to force register the task, or if the task is fine and nothing needs to happen. Whatever happens, we will either have a successful, resilient background task running or the user will know what they need to get it running by the time the navigationHelper_LoadState method completes.

This process is largely the same for the Windows app, except that we do not need to check if the app has been updated first. For reasons known only to Microsoft, we do not need to remove and then re-request background access when a Windows app has been updated — only Windows Phone apps have this requirement.

Here's the code for the navigationHelper_LoadState method in the MainPage.xaml.cs file of your Windows project:

...
private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
   	LoadLatestVideos();

  	var taskHelper = new TaskHelper();
 	string backgroundAccessStorageKey = "LastBackgroundAccessStatus";

   	//Check/Request background access
  	var request = await BackgroundExecutionManager.RequestAccessAsync();

  	if (request.ToString() == "Denied")
 	{
  		await new MessageDialog("Your phone has disabled background tasks for this app. Without background tasks, the app cannot notify you of new videos or current Twitch streams. Please enable background access for this application by going to Settings => Battery Saver => Usage => Jesse Cox for Windows.", "Background Access").ShowAsync();
   	}
   	else
  	{
       	//Force register a new task if last background access check was not granted, or if the app was updated
      	var forceRegister = localSettings.Values[backgroundAccessStorageKey] as string != request.ToString();

       	taskHelper.RegisterTasks(forceRegister);
  	}

   	//Save the new background access status
	localSettings.Values[backgroundAccessStorageKey] = request.ToString();
}
...

You might be worried that we're requesting background access every time the page loads; doesn't that prompt the user to grant permission?

Luckily, that won't be a problem. Windows will only prompt the user to grant background access once. There's no danger of accidentally spamming them every time they open your app. However, we always want to know the current status of background access because our timer task is integral to the application. That's why we use RequestBackgroundAccess every time the page loads — so we can tell if our task is allowed to run.

And there we go! Our app is now fully functional with background task swag. It displays a list of the latest 10 Jesse Cox videos, and it scans YouTube for new videos every 15 or 30 minutes.

There are three other things to keep in mind when you're building your background tasks:

  1. Although we can use Visual Studio to test the background task, that doesn't mean that the background task is actually registered or that it will run on the user's device.
  2. Similarly, even if we use code to check that the background task is registered and exists, that doesn't mean it's actually allowed to run.
  3. Checking background access and registering a new task requires that the user had closed the app previously — because we placed the task registration in the navigationHelper_LoadState method. Right now we're not handling the case where they open the app without permission, minimize it, grant permission and the resume the app. If you want to handle this situation, it would be easy to show a special link or button on your app's page that they can press to check/grant background access.

Once again, the code for this entire app is available on GitHub. The app has been uploaded to the Windows store and the Windows Phone store).


Learn how to build rock solid Shopify apps with C# and ASP.NET!

Did you enjoy this article? I wrote a premium course for C# and ASP.NET developers, and it's all about building rock-solid Shopify apps from day one.

Enter your email here and I'll send you a free sample from The Shopify Development Handbook. It'll help you get started with integrating your users' Shopify stores and charging them with the Shopify billing API.

We won't send you spam. Unsubscribe at any time.