Windows Phone Mango – Background Agents

There is often the need of executing code in the background while your application is not in the foreground. You might want to synchronize data through WCF or just simply alert the user of something. In Windows Phone Mango we have “multitasking” in the sense that scheduled tasks and background agents allow an application to execute code. This is however not a true multitasking feature since there are limits on how often and how long each task man run.

An application may only have one background agent registered and capable of performing periodic tasks, and/or resource intensive tasks. Depending on what type of task you implement they will be scheduled differently.

Let’s start by explaining the different tasks and when to use them, and then follow up on how you actually implement background agents.

PeriodicTask

Periodic agents run for a small amount of time (typically for 25 seconds) on regular reoccurring intervals (typically every 30 minutes). Use this type to do small tasks, small data synchronizations etc.

ResourceIntensiveTask

Resource intensive agents run for a relatively long period (typically 10 minutes), although only when certain constraints are met:

  • External power required
  • Non-Cellular connection required (needs Wi-Fi connection or through PC connection)
  • Minimum battery power (needs to be greater than 90%)
  • Device screen lock required (device screen needs to be locked)
  • No active phone call
  • Cannot change network to cellular

As we see there are a lot of constraints that needs to be met for resource intensive tasks to be executed, so most applications will only use period tasks.

Unsupported APIs

There are also a lot of APIs that you cannot use when executing code in a background agent. Take a look at the MSDN page for details: http://msdn.microsoft.com/en-us/library/hh202962%28v=VS.92%29.aspx

How to implement Background Agents

First of all you create a Windows Phone project and implement your application as usual. Next you need to add a Scheduled Task Agent project to your solution to add background agents use to your application

  1. From the File menu select Add-New Project and select Windows Phone Scheduled Task Agent project. Name the project and hit OK.
  2. Now in your Windows Phone project you need to add a project reference to the agent project.

In the Scheduled task agent project there is now a file called ScheduledAgent.cs. This file contains the source code that will be executed in the background. Add functionality to method OnIvoke() that you want to be executed, this method is called by the OS when the scheduled tasks executes.

Each application can only register one background agent, although you can implement both Periodic tasks and Resource Intensive Tasks. The below code section shows how you check task type and how to run the task more often when debugging. Since the tasks are executed each 30 min, it would be cumbersome to test your application if that period could not be adjusted. Luckily you can set that during debugging by calling ScheduledActionService.LaunchForTest() method.


protected override void OnInvoke(ScheduledTask task)
{

//TODO: Add code to perform your task in background

// If your application uses both PeriodicTask and ResourceIntensiveTask
// you can branch your application code here. Otherwise, you don't need to.
if (task is PeriodicTask)
{
// Execute periodic task actions here.
}
else
{
// Execute resource-intensive task actions here.
}

// If debugging is enabled, launch the agent again in one minute.
#if DEBUG_AGENT
ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(60));
#endif

// Call NotifyComplete to let the system know the agent is done working.
NotifyComplete();
}

Now to actually use background agents we have to register and add a background agent in our foreground application. This is also fairly straightforward to accomplish. You simply create either a PeriodicTask object or ResourceIntensiveTask object and add them to the ScheduledActionService.

Create PeriodicTask

PeriodicTask periodicTask;
string periodicTaskName = "PeriodicAgent";

private void StartPeriodicAgent()
{
  // Obtain a reference to the period task, if one exists
  periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;

  // If the task already exists and background agents are enabled for the
  // application, you must remove the task and then add it again to update
  // the schedule
  if (periodicTask != null)
  {
    RemoveAgent(periodicTaskName);
  }

  periodicTask = new PeriodicTask(periodicTaskName);

  // The description is required for periodic agents. This is the string that the user
  // will see in the background services Settings page on the device.
  periodicTask.Description = "This demonstrates a periodic task.";

  // Place the call to Add in a try block in case the user has disabled agents.
  try
  {
    ScheduledActionService.Add(periodicTask);
    PeriodicStackPanel.DataContext = periodicTask;

    // If debugging is enabled, use LaunchForTest to launch the agent in one minute.
#if(DEBUG_AGENT)
    ScheduledActionService.LaunchForTest(periodicTaskName, TimeSpan.FromSeconds(60));
#endif
  }
  catch (InvalidOperationException exception)
  {
    if (exception.Message.Contains("BNS Error: The action is disabled"))
    {
      MessageBox.Show("Background agents for this application have been disabled by the user.");
    }
  }
}

Create ResourceIntesiveTask

ResourceIntensiveTask resourceIntensiveTask;
string resourceIntensiveTaskName = "ResourceIntensiveAgent";

private void StartResourceIntensiveAgent()
{
  resourceIntensiveTask = ScheduledActionService.Find(resourceIntensiveTaskName) as ResourceIntensiveTask;

  // If the task already exists and background agents are enabled for the
  // application, you must remove the task and then add it again to update 
  // the schedule
  if (resourceIntensiveTask != null)
  {
    RemoveAgent(resourceIntensiveTaskName);
  }

  resourceIntensiveTask = new ResourceIntensiveTask(resourceIntensiveTaskName);

  // The description is required for periodic agents. This is the string that the user
  // will see in the background services Settings page on the device.
  resourceIntensiveTask.Description = "This demonstrates a resource-intensive task.";

  // Place the call to Add in a try block in case the user has disabled agents
  try
  {
    ScheduledActionService.Add(resourceIntensiveTask);
    ResourceIntensiveStackPanel.DataContext = resourceIntensiveTask;

    // If debugging is enabled, use LaunchForTest to launch the agent in one minute.
#if(DEBUG_AGENT)
    ScheduledActionService.LaunchForTest(resourceIntensiveTaskName, TimeSpan.FromSeconds(60));
#endif
  }
  catch (InvalidOperationException exception)
  {
    if (exception.Message.Contains("BNS Error: The action is disabled"))
    {
      MessageBox.Show("Background agents for this application have been disabled by the user.");
    }
  }
}

That’s all there is to it. Pretty simple and straightforward to implement.

In the OnInvoke() method you can call WCF services, do simple synchronizations and store data in Isolated Storage or the local SQL DB or what ever fits you needs. One thing that I have not figured out yet is if it is possible to alert the foreground application that data has changed and that it needs to refresh data from, for example, Isolated Storage.

Happy coding!

Advertisements
  1. thank for your article.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: