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.

Share

About Jesse Liberty

Jesse Liberty is an independent consultant and programmer with three decades of experience writing and delivering software projects. He is the author of 2 dozen books and multiple Pluralsight courses, and has been 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 Xamarin Certified Mobile Developer and a Xamarin MVP, Microsoft MVP and Telerik MVP.
This entry was posted in Background Agents, Mango, Multitasking and tagged . Bookmark the permalink.

5 Responses to Background Agents

  1. free sex says:

    hello there and thank you to your info ? I’ve definitely picked up something new from proper here. I did on the other hand expertise some technical points the usage of this web site, since I skilled to reload the web site a lot of times prior to I may just get it to load correctly. I were pondering if your web host is OK? Now not that I am complaining, however sluggish loading instances times will often impact your placement in google and could damage your high quality ranking if advertising and marketing with Adwords. Anyway I’m including this RSS to my e-mail and could look out for much more of your respective interesting content. Make sure you replace this once more very soon..

  2. Yama says:

    I see that only Periodic Task appear in the Background Tasks setting in the phone. Why is that

  3. Moin says:

    Hi. I understand the use of background agents but I had a scenario in mind and wanted help in understanding how to realize this in windows phone. Suppose my background task checks for location of the phone and does some job when the location matches. How should I do this. Periodic tasks do not run on 256MB devices. They do not get invoked if battery is low. These tasks typically run every 30min. But the location can change every sec or in couple of sec. Please help me with this, I am new to windows phone development.

Leave a Reply

Your email address will not be published.