Blog
Feature
12 de janeiro de 20269 min

Notificações Android: recorrência, distintivos e gamificação

As notificações em um gerenciador de tarefas são críticas. Se o lembrete não chegar no momento certo, o aplicativo falhou. E no Android, fazer um lembrete chegar no momento certo é uma batalha constante contra o próprio sistema operacional.

Não é um problema de código. É um problema de ecossistema. Samsung, Xiaomi, Huawei, Oppo: cada fabricante implementa sua própria política de economia de bateria que pode encerrar seu aplicativo e suas notificações agendadas sem aviso prévio. Adicione os distintivos de ícone, os push FCM e as notificações de gamificação, e você obtém um sistema que deve ser confiável, multiplataforma e emocionalmente satisfatório.

Pontos chave
  • Os lembretes recorrentes agendam as próximas 30 ocorrências de uma vez para sobreviver ao encerramento do Android, em vez de agendar apenas a próxima ocorrência.
  • Cada abertura do aplicativo verifica e reagenda as notificações perdidas durante a reinicialização ou encerramento pelo sistema.
  • Os distintivos de ícone no Android exigem implementações específicas por launcher (Samsung, Nova, AOSP), sem uma API unificada.
  • As notificações de gamificação transformam um mecanismo do sistema em um momento de motivação através de mensagens personalizadas e ícones customizados.
Smartphone em uma mesa exibindo distintivos de notificação e alertas de lembretes empilhados, despertador ao lado, luz matinal

Por que agendar 30 ocorrências em vez de apenas uma?

A resposta se resume a uma palavra: sobrevivência. No Android, o sistema operacional pode decidir encerrar seu aplicativo a qualquer momento para liberar memória. Quando o aplicativo é encerrado, a lógica JavaScript que deveria agendar a próxima ocorrência morre com ele.

A estratégia ingênua é agendar apenas o próximo lembrete e reagendar o seguinte quando o primeiro for acionado. Problema: se o aplicativo for encerrado entre o acionamento do lembrete e o reagendamento, a cadeia é quebrada. O usuário nunca mais receberá um lembrete.

Minha solução: agendar as próximas 30 ocorrências assim que o lembrete recorrente for criado. Se você tem um lembrete diário às 9h, o aplicativo cria imediatamente 30 alarmes: um para amanhã às 9h, um para depois de amanhã às 9h, e assim por diante. Mesmo que o aplicativo seja encerrado por 3 semanas, os lembretes continuarão a chegar.

A documentação Android sobre AlarmManager recomenda usar setExactAndAllowWhileIdle para alarmes críticos. É isso que uso para cada ocorrência. O "AllowWhileIdle" é crucial: sem ele, o modo Doze do Android (ativado quando o telefone está inativo e desconectado) pode adiar o alarme indefinidamente.

Por que 30 e não 365? Porque muitos alarmes simultâneos podem impactar o desempenho e a bateria. 30 dias é o compromisso ideal: o usuário está coberto por um mês, e o aplicativo atualiza as ocorrências a cada abertura. Eu já havia explorado esse tipo de compromisso desempenho/confiabilidade no artigo sobre a recorrência de lembretes.

Como gerenciar notificações perdidas na reinicialização?

Quando um telefone reinicia, todos os alarmes agendados via AlarmManager são perdidos. Este é o comportamento padrão do Android. A solução oficial: ouvir o broadcast BOOT_COMPLETED para reagendar os alarmes na inicialização.

Em teoria, é limpo. Na prática, é um campo minado.

Diferentes telefones Android de vários fabricantes alinhados em uma mesa, cada um mostrando comportamentos de notificação diferentes

A Samsung bloqueia BOOT_COMPLETED por padrão em alguns modelos com sua "Otimização de bateria". A Xiaomi tem o MIUI que impede a inicialização automática de aplicativos, a menos que o usuário a autorize manualmente nas configurações. A Huawei com EMUI faz o mesmo. A Oppo com ColorOS, idem. De acordo com o site Don't Kill My App, que lista os comportamentos por fabricante, alguns dispositivos bloqueiam até 90% dos broadcasts.

Minha solução pragmática: não depender apenas de BOOT_COMPLETED. A cada abertura do aplicativo, o NotificationService executa uma auditoria completa:

  1. Recuperação dos lembretes ativos do banco de dados local.
  2. Verificação: para cada lembrete, o alarme correspondente ainda existe no AlarmManager?
  3. Reagendamento dos alarmes ausentes.
  4. Limpeza dos alarmes obsoletos (tarefas excluídas, lembretes desativados).

Este processo leva cerca de 200ms para 50 lembretes ativos. O usuário não percebe. Mas isso garante que, mesmo que o telefone tenha reiniciado, mesmo que o fabricante tenha bloqueado o boot broadcast, as notificações serão reagendadas na próxima abertura do aplicativo.

Como o Firebase Cloud Messaging gerencia os push de grupo?

As notificações locais (lembretes, recorrência) são gerenciadas pelo AlarmManager no dispositivo. As notificações push (atividade de grupo, distintivos desbloqueados, menções) passam pelo Firebase Cloud Messaging.

O fluxo é o seguinte:

  1. Um usuário cria uma tarefa em um grupo e a atribui a um membro.
  2. O backend detecta a atribuição e recupera o token FCM do atribuído do banco de dados.
  3. O backend envia uma mensagem FCM com o título, o corpo e um payload de dados (tipo de ação, ID da tarefa, ID do grupo).
  4. O telefone do atribuído recebe o push, mesmo que o aplicativo esteja fechado.
  5. Se o usuário tocar na notificação, o aplicativo abre diretamente na tarefa em questão graças ao deep link no payload.

A principal armadilha com o FCM: os tokens mudam. Um token FCM pode se tornar inválido quando o usuário desinstala e reinstala o aplicativo, quando ele apaga os dados do aplicativo, ou quando o Google decide reciclá-lo. Minha estratégia: atualizar o token a cada lançamento do aplicativo e compará-lo com o armazenado no DB. Se diferente, atualizar. E no backend, cada erro de envio FCM com o código messaging/registration-token-not-registered aciona uma limpeza automática do token inválido.

É o mesmo princípio de resiliência que aplico no sistema de segurança e rate limiting: antecipar as falhas, não apenas gerenciá-las quando elas ocorrem.

Por que os distintivos de ícone são tão difíceis no Android?

O pequeno círculo vermelho com um número no ícone do aplicativo. No iOS, é uma linha de código: UIApplication.shared.applicationIconBadgeNumber = 5. É uma API de sistema padronizada desde o iOS 3.

No Android, não existe uma API padronizada para distintivos de ícone. Cada launcher implementa seu próprio método:

  • Samsung (OneUI): usa o Samsung BadgeProvider via um ContentProvider específico.
  • Nova Launcher: lê os distintivos de um broadcast Intent customizado.
  • AOSP/Pixel: usa o canal de notificação com setNumber() na própria notificação (sem distintivo autônomo).
  • Xiaomi (MIUI): tem sua própria API via um ContentProvider diferente da Samsung.
  • Huawei: usa os Huawei Mobile Services em vez dos Google Play Services.

A biblioteca react-native-app-badge abstrai parte dessa fragmentação, mas não cobre todos os casos. Aceitei essa realidade: em alguns dispositivos, os distintivos funcionarão perfeitamente. Em outros, eles não serão exibidos. O aplicativo nunca deve depender exclusivamente dos distintivos para comunicar informações importantes; eles são um bônus visual, não um canal crítico.

Como as notificações de gamificação transformam a experiência?

Tela de smartphone exibindo um desbloqueio de recompensa com confetes dourados e um ícone de troféu brilhante, tema escuro com detalhes dourados

As notificações mais satisfatórias não são os lembretes. São as notificações de gamificação. Um distintivo desbloqueado, um marco de sequência alcançado, um level up: são momentos de celebração que o usuário não esperava.

Implementei três tipos de notificações de gamificação:

Distintivo desbloqueado

Notificação local com um ícone customizado correspondente ao distintivo. O texto é personalizado: "Distintivo Organizador Pro desbloqueado! Você criou 100 tarefas." É mais envolvente do que "Novo distintivo desbloqueado". A personalização da mensagem aumenta a taxa de retenção da notificação, de acordo com pesquisas da OneSignal sobre personalização.

Marco de sequência

"Sequência de 30 dias! Continue assim." A sequência é um contador de dias consecutivos de uso do aplicativo. O sistema reconhece marcos: 7 dias, 30 dias, 100 dias, 365 dias. Cada marco aciona uma notificação e pontos bônus. Detalhei todo o sistema de gamificação no artigo sobre o esquema de gamificação.

Level up

"Nível 5 alcançado! Você agora é um Estrategista." Os 12 níveis têm cada um um nome e um limite de pontos. A notificação de level up é a mais rara e a mais gratificante. É o mesmo mecanismo de recompensa variável descrito por B.F. Skinner em suas pesquisas sobre reforço: recompensas imprevisíveis criam um engajamento mais forte do que recompensas previsíveis.

O ponto comum entre esses três tipos: eles transformam uma notificação (mecanismo frequentemente percebido como intrusivo) em um momento positivo. O usuário não sofre a notificação, ele a espera. Essa é exatamente a filosofia do feed de gamificação: tornar o engajamento visível e gratificante.

Como otimizar a confiabilidade das notificações em todos os dispositivos?

Se eu tivesse que resumir em uma frase: não confie em nada. Não confie em BOOT_COMPLETED. Não confie em AlarmManager no modo Doze. Não confie nos tokens FCM. Não confie nos distintivos de ícone.

A estratégia de defesa em profundidade:

  • Camada 1: alarmes locais via setExactAndAllowWhileIdle (30 ocorrências pré-agendadas).
  • Camada 2: auditoria e reagendamento a cada abertura do aplicativo.
  • Camada 3: push FCM como backup para lembretes críticos (enviados pelo backend se o alarme local não foi confirmado).
  • Camada 4: orientação do usuário para as configurações de bateria do fabricante (tela dedicada nas configurações do aplicativo).

Este último ponto é frequentemente negligenciado. O aplicativo inclui uma tela que detecta o fabricante do telefone e guia o usuário passo a passo para desativar a otimização de bateria para TAMSIV. Isso é UX técnica: explicar ao usuário por que seus lembretes podem não funcionar e como corrigi-los. É a mesma transparência que na onboarding lazy registration: ser honesto sobre as limitações em vez de escondê-las.

Perguntas frequentes

Os lembretes recorrentes funcionam quando o telefone está desligado?

Não. Nenhum alarme pode ser acionado quando o telefone está desligado. No entanto, assim que o telefone reinicia, o aplicativo reagenda automaticamente as ocorrências perdidas na próxima abertura. Os lembretes passados não são enviados retroativamente.

Por que as notificações às vezes atrasam em alguns telefones?

Isso está relacionado ao modo Doze do Android e às otimizações de bateria dos fabricantes. TAMSIV usa setExactAndAllowWhileIdle para minimizar atrasos, mas alguns fabricantes (notadamente Xiaomi e Huawei) impõem restrições adicionais. A seção "Notificações" das configurações do aplicativo explica como desativá-las.

As notificações de gamificação podem ser desativadas?

Sim. As configurações do aplicativo permitem desativar independentemente as notificações de distintivos, sequências e level up. Os lembretes de tarefas e os push de grupo estão em canais separados e não são afetados.

A sequência é perdida se eu perder um dia?

A sequência volta a zero se você perder um dia. No entanto, o sistema de "congelamento de sequência" permite que você proteja sua sequência: se você acumulou pontos suficientes, pode ativar um congelamento que preserva sua sequência por um dia de inatividade.

Os distintivos de ícone mostram o número exato de notificações não lidas?

Na Samsung e em launchers compatíveis, sim, o distintivo exibe o número exato. Em launchers AOSP padrão, o distintivo é um simples ponto (presença/ausência) sem número. Esta é uma limitação do Android que o Google ainda não unificou.