Porting a Windows Phone 8.0 Silverlight app to Windows Phone 8.1 Universal app - Background Tasks

After we handled Background Transfers in the previous post it took me some time to understand the differences between Background Tasks and the Background Agent.

The plain basics

We always started with a simple Class Library, we are a little bit more constrained now because we have to add a Windows Runtime Component. Then we add a reference to this newly created project from our app project.

image

In the past we’ve had to derive from ScheduledTaskAgent and override the OnInvoke method. When we were ready we did a NotifyComplete.

public class ScheduledBackgroundTask : ScheduledTaskAgent
{
    protected override void OnInvoke(ScheduledTask task)
    {
        //Do lots of work
        NotifyComplete();
    }
}

But things are different, but the look very similar. I have to implement IBackgroundTask and implement the Run method. I need to explicitly get a deferral and call the Complete method on the deferral when I’m ready. However don’t forget to make the class sealed.

public sealed class ScheduledBackgroundTask : IBackgroundTask
{
    public void Run(IBackgroundTaskInstance taskInstance)
    {
        var deferral = taskInstance.GetDeferral();
        //Do lots of work
        deferral.Complete();
    }
}

Registration

Registering of a task in Windows Phone 8.0 is very simple and straightforward there are PeriodicTasks and ResourceIntensiveTasks.

var periodicTask = new PeriodicTask(taskName)
{
    Description = taskDescription
};

var intensiveTask = new ResourceIntensiveTask(intensiveTaskName)
                {
                    Description = taskDescription
                };

ScheduledActionService.Add(periodicTask);
ScheduledActionService.Add(intensiveTask);

 

The registration is much more flexible now in the Universal app platform. As a start the background task needs to be added to the appxmanifest declarations. Here we also choose Timer and System event as supported task types. Only the Entry point is required, which we fill with the fully qualified name of the background task class.

image

Besides registering a background task, there’s the need to actually define the Trigger. There are lots of different triggers, but I focused on the TimeTrigger and the MaintenanceTrigger. The TimeTrigger makes your background task to run as often as every 15 minutes. I thought the MaintenaceTrigger was a lot like the ResourceIntensiveTask we have in Windows Phone 8.0, but more on that at the end of this article.

First we have to Request execution of Background Tasks. For Windows Phone this means access is granted silently. In big Windows this is different. Your app can only run in the same sense like Windows Phone background tasks when they are added to the lockscreen. Otherwise they will also run in the background, but only when the app is in the foreground. We define the Task Entry Point which is the fully qualified name of the background task class, I’ve seen a lot of handwritten strings in there, which I hate. Then we create a BackgroundTaskBuilder with a name and the created TaskEntryPoint. We have to set the trigger, to port my ScheduledTask this has to be a TimeTrigger. To complete registration we call the Register method on the TaskBuilder.

await BackgroundExecutionManager.RequestAccessAsync();

var taskEntryPoint = typeof(ScheduledBackgroundTask).FullName;
BackgroundTaskBuilder taskBuilder = new BackgroundTaskBuilder { Name = "Background Task", TaskEntryPoint = taskEntryPoint };

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

BackgroundTaskRegistration task = taskBuilder.Register();

 

Something I don’t need for porting my podcast app is the use of Conditions. This can be very interesting for your app so I suggest reading more about the Conditions.

App Update

While reading the documentation I found a small section I almost missed. It’s about what to do when the app launches after being updated. The app must explicitly call RemoveAccess on the background task when the version number changes. The below code is copied directly from MSDN because it just works.

async void 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 (Windows.Storage.ApplicationData.Current.LocalSettings.Values["AppVersion"] != appVersion)
    {
        // Our app has been updated
        Windows.Storage.ApplicationData.Current.LocalSettings.Values["AppVersion"] = appVersion;

        // Call RemoveAccess
        BackgroundExecutionManager.RemoveAccess();
    }

    BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();
}

Note: This is only valid for the Windows Phone apps.

There is a very interesting Trigger that calls a specific background task when an app update is downloaded. This can be used for example to apply database schema-changes or cleanup of old stuff. A good explanation of how background tasks work after an app update is written in the MSDN documentation.

Too many background tasks registered?

In Windows Phone 8.0 we had to catch the InvalidOperationException when Registering the Background Task. This was to get to know when for example too many background tasks are registered. In that particular case you would have check the message if it contained a message like “BNS Error: The maximum number of ScheduledActions of this type have already been added.”. Awful in my opinion, but it did work.

var periodicTask = new PeriodicTask(taskName)
{
    Description = taskDescription
};

try
{
    ScheduledActionService.Add(periodicTask);
}

catch (InvalidOperationException exception)
{
    if (exception.Message.Contains(
            "BNS Error: The maximum number of ScheduledActions of this type have already been added."))
    {
        // No user action required. The system prompts the user when the hard limit of periodic tasks has been reached.
    }
}

 

But this has greatly improved in the Universal app platform. As mentioned before, we have to explicitly request the execution of background tasks. This request will return a BackgroundAccessStatus.Denied if already too many background tasks are registered.

BackgroundAccessStatus registrationResult = await BackgroundExecutionManager.RequestAccessAsync();

if (registrationResult == BackgroundAccessStatus.Denied)
{
    //the maximum number of apps with background tasks across the system has been exceeded 
    //or the user has explicitly denied background task permissions for your app in the device's settings
}

Want more resources?

If we wanted more resources in Windows Phone 8.0 we registered a ResourceIntensiveTask instead of a PeriodicTask. This would help us with a BackgroundTask that can run for 10 minutes instead of the 25 seconds for the PeriodicTask.

Sounded really interesting but the constraints were very tight, in reality ResourceIntensiveTasks are only run when the phone was plugged in for a longer period of time, during the night for example.

Windows Universal apps don’t really have an equivalent for the ResourceIntensiveTask. Some Devs have pointed me towards the direction of a MaintenanceTrigger. The MaintenanceTrigger only fires when the power is plugged in, so that’s similar.

However it won’t get 10 minutes of time to execute. There’s not even a difference in execution time depending on the trigger. You’ll get 2 seconds of cpu time, which is completely different to clock time.

To get even worse, while I was testing with both the TimeTrigger and the MaintenaceTrigger, they are executed together when I plugin the power. This is something I didn’t expect because in Windows Phone 8.0 we either had a PeriodicTask or a ResourceIntensiveTask which got executed. For now I’m not implementing the ResourceIntensiveTask parts, let’s see if they are required in the end.

Gravatar