Criando serviços Android

Este guia aborda os serviços Xamarin.Android, que são componentes do Android que permitem que o trabalho seja feito sem uma interface do usuário ativa. Os serviços são muito comumente usados para tarefas executadas em segundo plano, como cálculos demorados, download de arquivos, reprodução de música e assim por diante. Ele explica os diferentes cenários para os quais os serviços são adequados e mostra como implementá-los para executar tarefas em segundo plano de execução longa, bem como para fornecer uma interface para chamadas de procedimento remoto.

Visão geral dos Serviços Android

Aplicativos móveis não são como aplicativos da área de trabalho. As áreas de trabalho têm grandes quantidades de recursos, como imóveis de tela, memória, espaço de armazenamento e uma fonte de alimentação conectada, dispositivos móveis não. Essas restrições forçam os aplicativos móveis a se comportarem de forma diferente. Por exemplo, a tela pequena em um dispositivo móvel normalmente significa que apenas um aplicativo (ou seja, Atividade) é visível por vez. Outras Atividades são movidas para o segundo plano e enviadas para um estado suspenso em que não podem executar nenhum trabalho. No entanto, apenas porque um aplicativo Android está em segundo plano não significa que é impossível para o aplicativo continuar funcionando.

Os aplicativos Android são compostos por pelo menos um dos quatro componentes principais a seguir: Atividades, Receptores de Difusão, Provedores de Conteúdo e Serviços. As atividades são a base de muitos ótimos aplicativos Android porque fornecem a interface do usuário que permite que um usuário interaja com o aplicativo. No entanto, quando se trata de executar trabalhos simultâneos ou em segundo plano, as atividades nem sempre são a melhor opção.

O mecanismo principal para trabalho em segundo plano no Android é o serviço. Um serviço Android é um componente projetado para fazer algum trabalho sem uma interface do usuário. Um serviço pode baixar um arquivo, reproduzir música ou aplicar um filtro a uma imagem. Os serviços também podem ser usados para comunicação entre processos (IPC) entre aplicativos Android. Por exemplo, um aplicativo Android pode usar o serviço de player de música que é de outro aplicativo ou um aplicativo pode expor dados (como informações de contato de uma pessoa) para outros aplicativos por meio de um serviço.

Os serviços e sua capacidade de executar trabalhos em segundo plano são cruciais para fornecer uma interface do usuário suave e fluida. Todos os aplicativos Android têm um thread main (também conhecido como thread de interface do usuário) no qual as Atividades são executadas. Para manter o dispositivo responsivo, o Android deve ser capaz de atualizar a interface do usuário à taxa de 60 quadros por segundo. Se um aplicativo Android executar muito trabalho no thread main, o Android removerá quadros, o que, por sua vez, fará com que a interface do usuário pareça irregular (também às vezes conhecida como janky). Isso significa que qualquer trabalho executado no thread de interface do usuário deve ser concluído no intervalo de tempo entre dois quadros, aproximadamente 16 milissegundos (1 segundo a cada 60 quadros).

Para resolver essa preocupação, um desenvolvedor pode usar threads em uma Atividade para executar algum trabalho que bloqueie a interface do usuário. No entanto, isso pode causar problemas. É muito possível que o Android destrua e recrie as várias instâncias da Atividade. No entanto, o Android não destruirá automaticamente os threads, o que pode resultar em vazamentos de memória. Um exemplo primo disso é quando o dispositivo é girado – o Android tentará destruir a instância da Atividade e, em seguida, recriar uma nova:

Quando o dispositivo gira, a instância 1 é destruída e a instância 2 é criada

Esse é um possível vazamento de memória – o thread criado pela primeira instância da Atividade ainda estará em execução. Se o thread tiver uma referência à primeira instância da Atividade, isso impedirá que o Android faça a coleta de lixo do objeto. No entanto, a segunda instância da Atividade ainda é criada (o que, por sua vez, pode criar um novo thread). Girar o dispositivo várias vezes em rápida sucessão pode esgotar toda a RAM e forçar o Android a encerrar todo o aplicativo para recuperar memória.

Como regra geral, se o trabalho a ser executado deve sobreviver a uma Atividade, um serviço deverá ser criado para executar esse trabalho. No entanto, se o trabalho só for aplicável no contexto de uma Atividade, a criação de um thread para executar o trabalho poderá ser mais apropriada. Por exemplo, a criação de uma miniatura para uma foto que acabou de ser adicionada a um aplicativo da galeria de fotos provavelmente deve ocorrer em um serviço. No entanto, um thread pode ser mais apropriado para reproduzir algumas músicas que só devem ser ouvidas enquanto uma Atividade está em primeiro plano.

O trabalho em segundo plano pode ser dividido em duas classificações amplas:

  1. Tarefa de Execução Prolongada – é um trabalho que está em andamento até ser interrompido explicitamente. Um exemplo de uma tarefa de execução prolongada é um aplicativo que transmite música ou que deve monitorar dados coletados de um sensor. Essas tarefas devem ser executadas mesmo que o aplicativo não tenha nenhuma interface do usuário visível.
  2. Tarefas periódicas – (às vezes chamada de trabalho) Uma tarefa periódica é relativamente curta em duração (vários segundos) e é executada em um agendamento (ou seja, uma vez por dia durante uma semana ou talvez apenas uma vez nos próximos 60 segundos). Um exemplo disso é baixar um arquivo da Internet ou gerar uma miniatura para uma imagem.

Há quatro tipos diferentes de serviços Android:

  • Serviço associado – um serviço associado é um serviço que tem algum outro componente (normalmente uma Atividade) associado a ele. Um serviço associado fornece uma interface que permite que o componente associado e o serviço interajam entre si. Depois que não houver mais clientes associados ao serviço, o Android desligará o serviço.

  • IntentService – Um IntentService é uma subclasse especializada da classe que simplifica a criação e o Service uso do serviço. Um IntentService destina-se a lidar com chamadas autônomas individuais. Ao contrário de um serviço, que pode lidar simultaneamente com várias chamadas, um IntentService é mais como um processador de fila de trabalho – o trabalho é enfileirado e um IntentService processa cada trabalho um de cada vez em um único thread de trabalho. Normalmente, umIntentService não está associado a uma Atividade ou a um Fragmento.

  • Serviço Iniciado – um serviço iniciado é um serviço que foi iniciado por algum outro componente do Android (como uma atividade) e é executado continuamente em segundo plano até que algo diga explicitamente ao serviço para parar. Ao contrário de um serviço associado, um serviço iniciado não tem nenhum cliente diretamente associado a ele. Por esse motivo, é importante projetar serviços iniciados para que eles possam ser reiniciados normalmente conforme necessário.

  • Serviço Híbrido – um serviço híbrido é um serviço que tem as características de um serviço iniciado e um serviço associado. Um serviço híbrido pode ser iniciado por quando um componente é associado a ele ou pode ser iniciado por algum evento. Um componente cliente pode ou não estar associado ao serviço híbrido. Um serviço híbrido continuará em execução até que seja explicitamente instruído a parar ou até que não haja mais clientes associados a ele.

Qual tipo de serviço usar depende muito dos requisitos do aplicativo. Como regra geral, um IntentService ou um serviço associado são suficientes para a maioria das tarefas que um aplicativo Android deve executar, portanto, a preferência deve ser dada a um desses dois tipos de serviços. Um IntentService é uma boa opção para tarefas "one-shot", como baixar um arquivo, enquanto um serviço associado seria adequado quando interações frequentes com uma Atividade/Fragmento são necessárias.

Embora a maioria dos serviços seja executada em segundo plano, há uma subcategoria especial conhecida como serviço em primeiro plano. Esse é um serviço que recebe uma prioridade mais alta (em comparação com um serviço normal) para executar algum trabalho para o usuário (como reproduzir música).

Também é possível executar um serviço em seu próprio processo no mesmo dispositivo, isso às vezes é conhecido como um serviço remoto ou como um serviço fora de processo. Isso requer mais esforço para criar, mas pode ser útil para quando um aplicativo precisa compartilhar funcionalidade com outros aplicativos e pode, em alguns casos, melhorar a experiência do usuário de um aplicativo.

Limites de execução em segundo plano no Android 8.0

A partir do Android 8.0 (nível de API 26), um aplicativo Android não tem mais a capacidade de ser executado livremente em segundo plano. Quando estiver em primeiro plano, um aplicativo poderá iniciar e executar serviços sem restrição. Quando um aplicativo é movido para o segundo plano, o Android concede ao aplicativo um determinado período de tempo para iniciar e usar serviços. Depois que esse tempo tiver decorrido, o aplicativo não poderá mais iniciar nenhum serviço e todos os serviços iniciados serão encerrados. Neste ponto, não é possível que o aplicativo execute nenhum trabalho. O Android considera um aplicativo em primeiro plano se uma das seguintes condições for atendida:

  • Há uma atividade visível (iniciada ou pausada).
  • O aplicativo iniciou um serviço em primeiro plano.
  • Outro aplicativo está em primeiro plano e está usando componentes de um aplicativo que, de outra forma, estaria em segundo plano. Um exemplo disso é se o Aplicativo A, que está em primeiro plano, está associado a um serviço fornecido pelo Aplicativo B. O aplicativo B também seria considerado em primeiro plano e não terminaria pelo Android por estar em segundo plano.

Há algumas situações em que, embora um aplicativo esteja em segundo plano, o Android ativará o aplicativo e relaxará essas restrições por alguns minutos, permitindo que o aplicativo execute algum trabalho:

  • Uma mensagem de nuvem do Firebase de alta prioridade é recebida pelo aplicativo.
  • O aplicativo recebe uma transmissão.
  • O aplicativo recebe e executa um PendingIntent em resposta a uma Notificação.

Os aplicativos Xamarin.Android existentes podem ter que alterar a forma como executam o trabalho em segundo plano para evitar problemas que possam surgir no Android 8.0. Aqui estão algumas alternativas práticas para um serviço Android:

  • Agendar o trabalho a ser executado em segundo plano usando o Agendador de Trabalhos do Android ou o Firebase Job Dispatcher – essas duas bibliotecas fornecem uma estrutura para aplicativos separarem o trabalho em segundo plano em trabalhos, uma unidade de trabalho discreta. Os aplicativos podem agendar o trabalho com o sistema operacional juntamente com alguns critérios sobre quando o trabalho pode ser executado.
  • Inicie o serviço em primeiro plano – um serviço em primeiro plano é útil para quando o aplicativo deve executar alguma tarefa em segundo plano e o usuário pode precisar interagir periodicamente com essa tarefa. O serviço em primeiro plano exibirá uma notificação persistente para que o usuário esteja ciente de que o aplicativo está executando uma tarefa em segundo plano e também fornece uma maneira de monitorar ou interagir com a tarefa. Um exemplo disso seria um aplicativo de podcasting que está reproduzindo um podcast para o usuário ou talvez baixando um episódio de podcast para que ele possa ser apreciado mais tarde.
  • Usar uma FCM (Mensagem de Nuvem do Firebase) de alta prioridade – quando o Android receber uma FCM de alta prioridade para um aplicativo, ele permitirá que esse aplicativo execute serviços em segundo plano por um curto período de tempo. Essa seria uma boa alternativa para ter um serviço em segundo plano que sonda um aplicativo em segundo plano.
  • Adiar o trabalho para quando o aplicativo entrar em primeiro plano – se nenhuma das soluções anteriores for viável, os aplicativos deverão desenvolver sua própria maneira de pausar e retomar o trabalho quando o aplicativo chegar ao primeiro plano.