Background Agents

Windows Phone Tutorial

Mango brings a number of new features to support background processing; many of which I’ll be covering in coming days and weeks.  The most general of all of these is the Background Agent.

Background Agents come in two flavors:

  • PeriodicTask
  • ResourceIntensiveTask

The PeriodicTask runs approximately every 30 minutes, for about 25 seconds.  This is perfect for updating tiles, sending toast notifications or otherwise keeping the user updated as to the status of, e.g., an application that retrieves data from a web service.

The ResourceIntensiveTask, on the other hand, run when certain conditions are met (the phone is connected to wifi or a computer rather than the cellular network, the phone is plugged into A/C rather than running on battery, the battery must in any case be at least 90% charged, etc.)  The advantage of the ResourceIntensiveTask is that it will run for approximately 10 minutes at a time, allowing for more, err, Resource Intensive tasks, such as synchronizing a local and a distributed database.

The actual setup and use of both types is very similar, as is made obvious by the Sample program on MSDN. This article strips that sample down to just the Periodic Task to make clear how easy it is to create and use a Background Agent.

The key to working with Background Agents is that you will create two projects in one solution:

  • The Main (foreground) project – which will contain the UI
  • A Task Agent project – with a class that derives from ScheduledTaskAgent

Fortunately, there is a Visual Studio template of type Windows Phone Scheduled Task Agent that creates just what we need for the second project.

To see this at work, begin by creating a Windows Phone project (using the Windows Phone Application template).  Name it BackgroundAgents.  Add a second project using the Windows Phone Scheduled Task Agent template.  You can use the default name of ScheduledTaskAgent1.

Critically important is that you create a reference to the second project in the first project (click on the foreground project, then click Add Reference…. and use the Project tab to find and select the project).

The Scheduled Task Agent project has one class, ScheduledAgent, which has one important method, OnInvoke.  This method is called whenever the Task is launched.  It is also critically important that you call NotifyComplete() when this method completes its work successfully or Abort() if it cannot complete its work.

Here’s the code we’ll add to OnInvoke, which will create a toast message and display it periodically (approximately every 30 minutes unless you are debugging, in which case, approximately every 30 seconds).

        protected override void OnInvoke( ScheduledTask task )
        {
            string toastMessage = "Periodic task running.";

            ShellToast toast = new ShellToast();
            toast.Title = "Background Agent Sample";
            toast.Content = toastMessage;
            toast.Show();

#if DEBUG_AGENT
            ScheduledActionService.LaunchForTest(
                task.Name, TimeSpan.FromSeconds(30));
#endif

            NotifyComplete();
        }

All that’s left to do is to fire off the task, which we do in the foreground application.  The key bit of Xaml is the checkbox that is used to start and stop the task,

<CheckBox
   Name="PeriodicCheckBox"
   IsChecked="{Binding IsEnabled}"
   Checked="PeriodicCheckBox_Checked"
   Unchecked="PeriodicCheckBox_Unchecked" />

The checked and unchecked event handlers are in the code behind,

private void PeriodicCheckBox_Checked(
    object sender, RoutedEventArgs e )
{
    StartPeriodicAgent();
}

private void PeriodicCheckBox_Unchecked(
    object sender, RoutedEventArgs e )
{
    RemoveAgent( periodicTaskName );
}

RemoveAgent is simpler, so let’s look at that first. YOu need only call Remove on the SecheduledActionService, passing in the name of the agent and catching any resulting exception (there is no action to take in the case of an exception except to not crash).

private void RemoveAgent( string name )
{
    try
    {
        ScheduledActionService.Remove( name );
    }
    catch (Exception)
    {
    }
}

The more interesting code is in StartPeriodicAgent; our method for firing up a PeriodicTask.  This is done in two steps:

  1. Remove the task if it already exists
  2. Add the task (and handle the exception if background agents for this application are disabled)
private void StartPeriodicAgent()
{
    AgentIsEnabled = true;

    // If this task already exists, remove it
    periodicTask =
        ScheduledActionService.Find( periodicTaskName )
        as PeriodicTask;
    if (periodicTask != null)
    {
        RemoveAgent( periodicTaskName );
    }

    periodicTask = new PeriodicTask( periodicTaskName );
    periodicTask.Description =
        "This demonstrates a periodic task.";

    try
    {
        ScheduledActionService.Add( periodicTask );
        PeriodicStackPanel.DataContext = periodicTask;

    #if(DEBUG_AGENT)
        ScheduledActionService.LaunchForTest(
            periodicTaskName,
            TimeSpan.FromSeconds( 20 ) );
    #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." );
            AgentIsEnabled = false;
            PeriodicCheckBox.IsChecked = false;
        }
    }
}

Finally, when we navigate to the page we test to see if this task already exists; if so we assign it as the datacontext for the display StackPanel,

 protected override void OnNavigatedTo(
     System.Windows.Navigation.NavigationEventArgs e )
 {

     periodicTask =
         ScheduledActionService.Find( periodicTaskName )
         as PeriodicTask;

     if (periodicTask != null)
     {
         PeriodicStackPanel.DataContext = periodicTask;
     }
 }

For completeness, here is the entire Xaml from the ContentPanel:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
 <StackPanel>
    <StackPanel
       Orientation="Vertical"
       Name="PeriodicStackPanel"
       Margin="0,0,0,40">
       <TextBlock
          Text="Periodic Agent"
          Style="{StaticResource PhoneTextTitle2Style}" />
       <StackPanel
          Orientation="Horizontal">
          <TextBlock
             Text="name: "
             Style="{StaticResource PhoneTextAccentStyle}" />
          <TextBlock
             Text="{Binding Name}" />
       </StackPanel>
       <StackPanel
          Orientation="Horizontal">
          <TextBlock
             Text="is enabled"
             VerticalAlignment="Center"
             Style="{StaticResource PhoneTextAccentStyle}" />
          <CheckBox
             Name="PeriodicCheckBox"
             IsChecked="{Binding IsEnabled}"
             Checked="PeriodicCheckBox_Checked"
             Unchecked="PeriodicCheckBox_Unchecked" />
       </StackPanel>
       <StackPanel
          Orientation="Horizontal">
          <TextBlock
             Text="is scheduled: "
             Style="{StaticResource PhoneTextAccentStyle}" />
          <TextBlock
             Text="{Binding IsScheduled}" />
       </StackPanel>
       <StackPanel
          Orientation="Horizontal">
          <TextBlock
             Text="last scheduled time: "
             Style="{StaticResource PhoneTextAccentStyle}" />
          <TextBlock
             Text="{Binding LastScheduledTime}" />
       </StackPanel>
       <StackPanel
          Orientation="Horizontal">
          <TextBlock
             Text="expiration time: "
             Style="{StaticResource PhoneTextAccentStyle}" />
          <TextBlock
             Text="{Binding ExpirationTime}" />
       </StackPanel>
       <StackPanel
          Orientation="Horizontal">
          <TextBlock
             Text="last exit reason: "
             Style="{StaticResource PhoneTextAccentStyle}" />
          <TextBlock
             Text="{Binding LastExitReason}" />
       </StackPanel>
    </StackPanel>
 </StackPanel>
</Grid>

See also this video.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Background Agents, Mango, Multitasking and tagged . Bookmark the permalink.

5 Responses to Background Agents

Comments are closed.