Periodic Agents on Windows Phone 7.1


One major feature that was much requested for the new version of Windows Phone was the ability to run agents behind the scenes. The desire was to be able to execute code periodically so that a developer’s application could keep itself up to date (or tell the user about a change) when the application was not running. Microsoft has allowed this in Mango (e.g. Windows Phone OS 7.1) and allows several different flavors of agents:

  • Periodic Agents: Code that run about every 30 minutes.
  • Resource Intensive Agents: Code that runs when the phone is plugged-in and has a WiFi connection (or when plugged into USB).
  • Audio Agents: Code that is run in the background to provide audio to the user (e.g. for Pandora-like applications).

For this post, I will focus on the Periodic Agents. Periodic agents run every 30 minutes but with some limitations:

  • Forbidden APIs (most anything to do with the UI like accessing the camera).
  • 5MB of Memory Maximum (OS will kill process if breaks this limit).
  • App must re-register agent every two weeks (e.g. if user doesn’t run your app every two weeks, it will cease to execute).

Creating a Periodic Agent

Periodic agents run as an included assembly in your .xap file (but a separate assembly from the startup assembly).  By picking File->New->Project you can pick the “Windows Phone Scheduled Task Agent” project type. This will create a new project in your solution and create a skeleton class for you:

using Microsoft.Phone.Scheduler;

namespace BackgroundAgent
{
  public class ScheduledAgent : ScheduledTaskAgent
  {
    protected override void OnInvoke(ScheduledTask task)
    {
      //TODO: Add code to perform your task in background

      NotifyComplete();
    }
  }
}

The OnInvoke call is where your code happens. Once your task has completed, you need to call the NotifyComplete method to tell the operating system you are completed with the background task. These are short tasks and will only run for a short period (typically less than 15 seconds) so making your code very concise is important.

Once you have the agent assembly, you need to associate it with your main project (so it’s included in the .xap file) by simply making a reference to it. In addition, the project template will delve into the WMAppManifest.xml file and add an “ExtendedTask” element that points a the type name of the background agent:

<Tasks>
  <DefaultTask Name="_default"
               NavigationPage="MainPage.xaml" />
  <ExtendedTask Name="MyBackgroundTask"> 
    <BackgroundServiceAgent Specifier="ScheduledTaskAgent"
                            Name="FooManMark"
                            Source="BackgroundAgent"
                            Type="BackgroundAgent.ScheduledAgent" />
  </ExtendedTask>
</Tasks>

Your application can have more than one ExtendedTask but each Extended Task has to support a different kind of background agent. Specifically the BackgroundServiceAgent’s Specifier property must be unique. So that means you can only have one scheduled agent and one of each of the audio agent types. The names of the ExtendedTask and BackgroundServiceAgent elements are not important so you do not have to keep these in sync with what you use in code as you will see next.

Adding Code to Your Agent

Now that your agent is ready to be deployed to a device, you can add your code to the OnInvoke method like so:

protected override void OnInvoke(ScheduledTask task)
{
  // If the Main App is Running, Toast will not show
  ShellToast popupMessage = new ShellToast()
  {
    Title = "My First Agent",
    Content = "Background Task Launched",
    NavigationUri = new Uri("/Views/DeepLink.xaml", UriKind.Relative)
  };
  popupMessage.Show();

  NotifyComplete();
}

Notice that you can run any code you need. In this case I am using a Toast message to alert the user. (As an aside, toast messages will only show if the main application is not running). As long as the code you have in here does not violate the memory, APIs or time limitations, you’re good to go.

Registering Your Agent

Finally, in your main application you will need to register the background service.  You can accomplish this with the

// A unique name for your task. It is used to 
// locate it in from the service.
var taskName = "MyTask";

// If the task exists
var oldTask = ScheduledActionService.Find(taskName) as PeriodicTask;
if (oldTask != null)
{
  ScheduledActionService.Remove(taskName);
}

// Create the Task
PeriodicTask task = new PeriodicTask(taskName);

// Description is required
task.Description = "This saves some data to Isolated Storage";

// Add it to the service to execute
ScheduledActionService.Add(task);

You will want to use a unique task name so you can find your background task when you need to register it. This name is unique to your application so you can make it whatever you want.  You can see in this example that the first thing I do is find the old task so I can remove it and replace it with a new task. Next I create a new PeriodicTask object that contains the information about the agent. The only required field is the Description property. The description is shown to users when they are reviewing background tasks in the OS’s management UI. This means the description should be in plain English that makes sense to end-users.

Finally you can schedule the background agent by adding it to the ScheduledActionService (same service that is used to schedule Alarms and Reminders). The information about what assembly to load and the name of the class is all read in from the WMAppManifest.xml file so you do not need to specify it here.

Once you do this, your service will start running approximately every 30 minutes (unless the user has disabled background tasks or battery saver is on).

Testing Your Agent

You could just wait 30 minutes to debug your background task but that is not very helpful. Instead the ScheduledActionService has a static method called LaunchForText. This method should be used to execute your task for debugging purposes:

var taskName = "MyTask";

ScheduledActionService.LaunchForTest(taskName, 
  TimeSpan.FromMilliseconds(1500));

The same name you used to register the agent should be used as the first parameter to the LaunchForText method. The second parameters let’s you specify how soon to execute the service. This is often used to give you time to kick off the launch and exit the application. The debugger will remain connected to let you actually debug your code. 

Only caveat about debugging agents is they can only be debugged once per session. If you need to re-run your agent, you’ll need to re-start the debugger.

Conclusion

Using these simple agents can help you accomplish specialized operations where you need code to be executed behind the scenes without hampering the performance or battery of the phone itself. You still don’t have a blank check to run background code, but this will fill out some significant use-cases.




Application Name WilderBlog Environment Name Production
Application Ver 1.1.0.0 Runtime Framework .NETCoreApp,Version=v1.1
App Path D:\home\site\wwwroot Runtime Version .NET Core 4.6.25211.01
Operating System Microsoft Windows 6.2.9200 Runtime Arch X86