Curso Windows Phone – Como desenvolver APPs com tarefas em segundo plano

4-apps-keep-on-task-on-time1Uma situação comum em APPs para dispositivos móveis e em especial para Windows Phone é a necessidade de termos tarefas relacionadas a um determinado aplicativo, que fiquem executando em segundo plano, independente se o usuário está com a APP ativa ou não. O uso desse tipo de recurso, permite por exemplo, sermos avisados de atualizações em um determinado blog, alterações na previsão do tempo, recepção de novos e-mails ou mensagens instantâneas, atualização de uma live tile, dentre tantas outras situações.

A implementação desse tipo de recurso em APPs para WP está relaciona ao uso de duas classes, que são:

  • PeriodicTask: as tarefas são executas por um pequeno período de tempo (máximo de 25 segundos por vez) em um intervalo regular e recorrente (por padrão a cada 30 minutos) e, caso a economia de bateria esteja ativada, as tarefas não serão executas.
    Cenários típicos para este tipo de tarefa incluem atualizar a geo localização do dispositivo e realizar pequenas sincronizações de dados.
  • ResourceIntensiveTask: são tarefas executas durante um longo período de tempo e sua execução depende de um conjunto de requisitos às atividades do processador, fonte de alimentação de energia e conexão de rede.
    Um cenário típico para este tipo de tarefa está na sincronização de grandes quantidades de dados para o telefone, enquanto ele não está sendo ativamente usado pelo usuário.

Em nosso exemplo trataremos sobre os agentes de segundo plano implementados através da classe PeriodicTask. Vamos a ele:

 1) Criando o projeto da APP com tarefas em segundo plano

Sua primeira providência é, via Visual Studio, adicionar um projeto do tipo Windows Phone Scheduled Task Agent ao projeto original de sua APP.

Novo-Projeto

Projeto-Task-Agent

2) Adicionando a codificação necessária no projeto do tipo Scheduled Task Agent

No projeto em questão, abra o arquivo ScheduledAgent.cs e nele observe a existência de um método chamado OnInvoke. Nesse método você deverá criar a codificação necessária para a tarefa que se deseja executar em segundo plano, por exemplo, o envio de um Toast, download de um conteúdo da web para atualização de uma live tile, etc.

No exemplo abaixo, simulo a criação de um toast que irá aparecer na tela do usuário sempre que a tarefa em segundo plano for executada, porém, para não termos que ficar esperando muito tempo para que isso ocorra, 30 minutos por padrão, defini que em modo de debug essa tarefa periódica irá ocorrer a cada 1 minuto, para isso repare a declaração da instrução abaixo logo na primeira linha da classe ScheduledAgent.cs e depois dentro do método OnInvoke sua utilização.


#define DEBUG_AGENT

//Confira se os namespaces abaixo estão declarados em sua classe, caso não estejam, declare-os
using Microsoft.Phone.Scheduler;
Using Microsoft.Phone.Tasks;

...

Declaração da codificação necessária para o envio do toast de exemplo no método OnInvoke


protected override void OnInvoke(ScheduledTask task)
{
  string mensagem = string.Empty;

  if (task is PeriodicTask) //Identificando o tipo de tarefa
  {
    //Incluir aqui todas as ações a serem realizadas pela periodic task
    mensagem = "Periodic task em execução...";
  }
  else
  {
    //Inlcuir aqui todas as ações a serem realizadas pela resource-intensive task
    mensagem = "Resource-intensive task em execução...";
  }

  ShellToast toast = new ShellToast();
  toast.Title = "Exemplo de agente em segundo plano.";
  toast.Content = mensagem;
  toast.Show();

  NotifyComplete();

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

 3) Implementando a interface XAML e a codificação C# para realização dos testes

Agora que já fizemos o necessário em relação ao agente de segundo plano (background) vamos desenvolver uma página Windows Phone simples para realização dos testes e, assim como feito no passo 2, vamos realizar a declaração de um modo DEBUG para não termos que ficar esperando os 30 minutos de intervalo padrão entre uma execução e outra da tarefa periódica (Periodic Task) enquanto estivermos na fase de desenvolvimento. Faça a declaração indicada abaixo na primeira linha de sua página de testes (MainPage.xaml.cs) e aproveite para verificar se o namespace indicado também está declarado.


#define DEBUG_AGENT

...

using Microsoft.Phone.Scheduler;

...

A seguir, declare no escopo da classe um objeto da classe PeriodicTask, uma variável que indicará o nome da tarefa de segundo plano a ser criada e mais algumas variáveis que serão utilizadas para controle da interface.


...

public partial class MainPage : PhoneApplicationPage
{
  PeriodicTask periodicTask;
  string periodicTaskName = "Agente Periódico";
  public bool agentsAreEnabled = true;
  bool ignoreCheckBoxEvents = false;
...

Na sequencia iremos definir a interface XAML com o usuário, ficando assim:

</pre>
...

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  <StackPanel Orientation="Vertical" Name="PeriodicStackPanel" Margin="0,0,0,40">
    <TextBlock Text="Periodic Agent" Style="{StaticResource PhoneTextTitle2Style}"/>
    <StackPanel Orientation="Horizontal">
      <TextBlock Text="Nome: " Style="{StaticResource PhoneTextAccentStyle}"/>
      <TextBlock Text="{Binding Name}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <TextBlock Text="Está ativo:" VerticalAlignment="Center" Style="{StaticResource PhoneTextAccentStyle}"/>
      <CheckBox Name="PeriodicCheckBox" IsChecked="{Binding IsEnabled}" Checked="PeriodicCheckBox_Checked" Unchecked="PeriodicCheckBox_Unchecked" />
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <TextBlock Text="Está agendado: " Style="{StaticResource PhoneTextAccentStyle}"/>
      <TextBlock Text="{Binding IsScheduled}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <TextBlock Text="Último agendamento: " Style="{StaticResource PhoneTextAccentStyle}"/>
      <TextBlock Text="{Binding LastScheduledTime}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <TextBlock Text="Expira em: " Style="{StaticResource PhoneTextAccentStyle}"/>
      <TextBlock Text="{Binding ExpirationTime}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <TextBlock Text="Última razão de saída: " Style="{StaticResource PhoneTextAccentStyle}"/>
      <TextBlock Text="{Binding LastExitReason}" />
    </StackPanel>
  </StackPanel>

</Grid>


private void StartPeriodicAgent()
{
  agentsAreEnabled = true;
  periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;

  if (periodicTask != null)
    RemoveAgent(periodicTaskName);

  periodicTask = new PeriodicTask(periodicTaskName);
  periodicTask.Description = "Isto demonstra uma tarefa periódica.";

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

#if (DEBUG_AGENT)
    ScheduledActionService.LaunchForTest(periodicTaskName,TimeSpan.FromSeconds(60));
#endif
  }
  catch(InvalidOperationException exception)
  {
    if (exception.Message.Contains("BNS Error: A ação está desabilitada."))
    {
      MessageBox.Show("Os agentes periódicos de segundo plano foram desabilitados pelo usuário da APP.");
      agentsAreEnabled = false;
      PeriodicCheckBox.IsChecked = false;
    }

    if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added."))
    {
       //Nenhuma ação é requerida. O sistema irá informar ao usuário que o limite de tarefas periódicas foi atingido.
    }

    PeriodicCheckBox.IsChecked = false;
  }
}

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

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

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

A última parte do código C# é a implementação do método abaixo para que possamos informar ao usuário qual o status do agente de segundo plano assim que ele entra na aplicação. Vejamos:


protected override void OnNavigatedTo(NavigationEventArgs e)
{
  ignoreCheckBoxEvents = true;
  periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;

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

  ignoreCheckBoxEvents = false;
}

4) Último passo

Por fim, vamos ajustar o arquivo  WMAppManifest.xml para especificar o agente de segundo plano ligado a APP. Localize a seção <Tasks>…</Tasks> e faça com que a mesma fique similar ao indicado abaixo:

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

No código XML acima, o nome TestScheduledTaskAgent é o nome dado para o projeto do tipo Windows Phone Scheduled Task Agent que você adicionou ao seu projeto original da APP.

Como resultado final de todo código acima, teremos uma APP onde dentro do período especificado para a PeriodicTask irá executar as tarefas existes no método OnInvoke.

periodic-task-sample

 

[Download do Projeto de Exemplo]

 

Grande abraço !
Eduardo Henrique Rizo

MCP

Post Relacionado: 

[twitter-follow screen_name=’eduardorizo’ show_count=’yes’]

 

 

3 comentários em “Curso Windows Phone – Como desenvolver APPs com tarefas em segundo plano”

  1. Pingback: Free: Curso Windows Phone – Vários tópicos | Blog do Eduardo H. Rizo

  2. Opa, ótimo post, eu testei mas só executa uma vez. Depois que limpo a lista de notificações não aparece mais. Como proceder? Obrigado

Deixe um comentário