[Desenvolvimento] Como desenvolver APPs com tarefas em segundo plano

6

Uma 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.

1
2
3
4
5
6
7
8
 
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 
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.

1
2
3
4
5
6
7
8
 
#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.

1
2
3
4
5
6
7
8
9
10
 
...
 
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
...
 
<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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
 
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:

1
2
3
4
5
6
7
8
9
10
11
 
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:

1
2
3
4
5
6
7
...
<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 (@eduardorizo)

MCP

Post Relacionado: 

Fonte: Blog do Eduardo H. Rizo

Share.

About Author

Bacharel em Ciência da Computação pela FIPP/Unoeste, Pós-Graduado em Desenvolvimento de Sistemas Web, Segurança da Informação e Avaliação do Ensino e da Aprendizagem, Professor universitário e responsável pelo setor de desenvolvimento de sistemas web da Universidade do Oeste Paulista. MCP e MCPS pela Microsoft e nas horas vagas dedica um pouco do seu tempo escrevendo posts técnicos sobre desenvolvimento de sistemas web, windows phone, gerenciamento de servidores e outros assuntos para a comunidade.

  • Parabéns pelo artigo, como sugestão da parte 2 dele, colocar o resultado da task em uma live tile.

  • Fábio Radicchi Belotto

    Outra pergunta, qual a diferença de linguagem do wp7, wp8 e wp8.1?

  • Fábio Radicchi Belotto

    Eu sei que a pergunta deve ser estúpida, mas ajuda aí.
    Qual é a linguagem usada no Windows Phone? Essa mesma linguagem serve para o Windows desktop?

    Qual é a linguagem usada no Android?

    Outra questão, a programação do Windows Phone tem alguma limitação ou a diferença de qualidade visual em apps de Android e wp é só preguiça de programador?

    • Alan

      Em questão de Interface é pura preguiça, Aplicativos como 6TAG, Toib / My Tube, entre outros possuem uma Interface muito legal.

      • Fábio Radicchi Belotto

        Esses apps são legais mas parecem que tem um visual simples. Eu tenho um wp8.1 e um kit kat 4.4.4. E parece que os apps do Android tem um visual mais caprichado, e isso é geral.
        Por isso que eu comecei a pensar que a limitação era a linguagem.

  • Wesley B. Peres

    Muito bom cara. Espero ano que vem ter tempo para aproveitar todos esses posts =)