Ranting and raving about anything I feel like complaining about.

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.

 
 

Comments

Gravatar Great article, like always!
Gravatar Nice Article,
I tried it on Microsoft Visual Studio 2010 Express For Windows Phone Emulator, LaunchForTest is not shown in ScheduledActionService class. Am I missing something?
Gravatar zis,

Likely you have an old version of the SDK. Make sure you have Windows Phone SDK 7.1 RC
Gravatar Let's say i want to make a kind of agenda, and want to check the time every 60 seconds instead of 30 minutes. What do you do ? The only is to use LaunchForTest on every OnInvoke (it works, i test it without the debug thing). I read that we can create alarms but my only concern is that you can create a RecurrenceType of i.e. "every monday"
Gravatar Well, there are a couple of things you're saying here. There is no way to get your agent to run every 60 seconds. It would have too big an impact on the battery life of the phone. If you need to run it that often, you'll need to use push notifications IMO.

Now the idea of using LaunchForTest is odd too. Because this will only work while your application is in the running (which isn't why you'd need the background agent at all).

So if you need to run it while your app is running, just execute the code, you don't need to invoke the agent at all. Just the code in the agent. But if you need it to run while your application isn't running, then you should look at push notifications.
Gravatar Hi,I want to create or find my Tile in OnInvoke,but I fail.
I even can not use ShellTile, How should I do?
Gravatar If I want to pole a URL every 6 minutes, are Periodic Agents the right way to do this on Phone 7 ?
Gravatar Will the ScheduledActionService.LaunchForTest() work in a released app?
Gravatar Andrew,

Not every six minutes...but periodically, yes.
Gravatar Sam,

Yes, but why would you? You can share the same code in your app that you could call. No reason to call the agent when you're app is running.
Gravatar What is the use of Periodic Agents if I cannot pass active agent more than 14 days. (current maximum limit). How do I extend it without user interaction?
Gravatar Ashraf,

The 14 day limitation is saying that user must launch the app at least once every two weeks. If you have a periodic agent but your app isn't needed once every two weeks, then the value of your app is the problem (IMO).

Typically this means that every time your app launches, you can just re-register your agent so that it resets the 14 days.
Gravatar Jack,

ShellTile works in a Periodic Agent. What are you seeing?
Gravatar Can't we re-register the agent in OnInvoke method?
Gravatar Shail,

No. That would defeat the limitation. Agents are extensions of apps, not apps themselves.
Gravatar I'm trying to have both Periodic and Resource Intensive agents. I've created two projects, one per each. However - when trying to add a reference to the second one, I get the "WMAppManifest.xml already contains a reference to this type of background agent. You cannot add more than one background agent of the same type".
1. How can I overcome this?
2. How do I set a type to each of the projects (as-far-as-I understand, I set the type only when adding the scheduled task)
Gravatar You can't have two projects. One project/agent and you can test which type of agent is running (periodic or resource) by testing the type of agent in the passed in parameters.
Gravatar I've done what you have above, and have my agent working. However, I need to access the settings class and the resources (it is a localized app) in the main project from the scheduled task project. If I try to add a reference, I get a message that this would create a circular dependency. Can't do that. Is there a way to add a scheduled task to the original project? Does it have to be a separate project?
Gravatar Rich,

You would need to move the settings and localized data into a 3rd, separate assembly to share between them.
Gravatar FYI on my prior comment - what I'm trying to do is update the shell tile, which works using strings, but I need to use my string resources since it is a localized app. I need access to my settings class (and actually one other) so that I can use them to calculate what to update the tile to show.
Gravatar Ah! Thanks!
Gravatar Shawn, I did as you said, and it works. It took me a quick search to figure out how to set up the xmlns but I got it. For future, with projects like this, would you recommend putting all the model classes into a separate project, if I'm doing something like this, where I have to have a separate project for say a background task? This is the first time I've done anything with more than one project in a solution, so I'm wondering what other purposes it could serve. Thanks!
Gravatar In app.xaml.cs:

private void RegisterBackgroundTask()
{
var taskName = "PlusPoints Updater";
var oldTask = ScheduledActionService.Find(taskName) as PeriodicTask;
if (oldTask != null)
{
ScheduledActionService.Remove(taskName);
}
PeriodicTask task = new PeriodicTask(taskName);
task.Description = "This task updates the PlusPoints live tile.";
//at this point there are no tasks in background tasks of phone settings
ScheduledActionService.Add(task);
//at this point, there are two tasks with app title, same description
//ScheduledActionService.LaunchForTest(taskName, TimeSpan.FromSeconds(15));
}

In app.xaml.cs constructor:
RegisterBackgroundTask();


In ScheduledAgent:

protected override void OnInvoke(ScheduledTask task)
{
UpdateTile();
NotifyComplete();
}
private void UpdateTile()
{
//do stuff
}
Gravatar Shawn, I found the problem with my task showing up twice. Somehow it got into the WMAppManifest.xml file twice. Not sure how that happened, but deleting one of them solved the problem. Just thought I'd let you know in case anyone else ever asks.
Gravatar Dear Shawn,
Thank you for the great tutorial and please excuse me for my English.
We want to advertise periodically the location to a server surely in a period less than 30 minutes. Using the suggestion of "SuNcO": by adding LaunchForTest in OnInvoke method seems to work for us perfectly. It seems that we can schedule the background agent as often as we want in release mode and with the phone disconnected from computer. This seems strange since microsoft didn't want background services to run that often.

Is there any problem using that solution (except the big impact to battery) that we can't see at the moment or is there anything else better we can do?

Thanks in advance,
Ze Gathem Team
Gravatar Hello and thank for the article.

you can debug multiple times the app without restarting it by calling the LaunchForTest method in the agent too.

Jonathan http://www.jonathanAntoine.com


Regards,

Gravatar Shawn,
You commented:
'The 14 day limitation is saying that user must launch the app at least once every two weeks. If you have a periodic agent but your app isn't needed once every two weeks, then the value of your app is the problem (IMO).'
I have a problem with this and the 14 day limitation. If your app is an alarm or a reminder or something similar and the date the user enters is greater than 14 days why should there be a need for the app to be opened again before that time? An app I'm working on has the user enter a date. Now if that date is greater than the 14 day value the background task will stop and my app will not function properly. There is no need for the user to open my app again until they would like to set up a second event reminder. Am I missing something?
Gravatar Phillip,

That's what alarms and reminders are for. Different use case that they've handled to deal with.
Gravatar Shawn,

Can an alarm or reminder update a live tile though? I'm new to windows phone development but I didn't think they could. The app creates a live tile based on some information the user enters. It then updates some of the information each day within the tile for the user. My point only was if the information entered was for an event a couple of months from now there would be no point for the user to open the app again as the live tile would now be the user's focal point. If this is the case then I don't believe the value of the app is the issue but a limitation with the OS. I probably wasn't very clear when I mentioned an alarm or reminder as those are different and should be handled differently.

The best solution I could come up with was to keep up with a count of days since the agent was registered and when it starts to reach the 14 day point notify the user on the live tile asking them to simply open the app. I just wish this limit was a little larger. 30 days would be nice and 60 would be even better. I just think 14 days is a little too short of a time period.
Gravatar I know you don't want to do this much, but updating the tile via Tile Notifications is the answer. Running code on my phone (and eating battery life) for two months to alarm me isn't worth it to me.
Gravatar Shawn,
When you mention using 'Tile Notifications' are you referring to push notifications? If so for me that is not possible at the moment. I am attempting this as a side project to see if I can create and publish an app. With that said I don't want this to be crappy or a hack job either. Maybe in the future but just not right now.

I do have a question for you. If you were creating an app that only needed to update a live tile once a day what would be your approach for
1. Absolute best approach.
2. Best approach contained and updated by the phone itself.

I have found that the ShellTileSchedule class will allow me to update the background image to a secondary tile on more defined schedule. I was under the impression this was for the app's tile only. I can use this as with some re-write I can make the only update to be the background image which I am going to test. I just wish you could define a more detailed schedule for the background agent as I don't want this to run every 30 minutes all day every day. Thanks for taking the time read and respond to these and thanks for the article as it did help me in getting the background agent created and running properly.

Add a Comment

*
*
*