Consolidar notificações em dispositivos móveis

A partir do nível 26 da API do Android, as notificações persistentes são necessárias para serviços em primeiro plano. Esse requisito visa evitar que você se esconda serviços que possam exigir excessivamente os recursos do sistema, incluindo bateria em particular. Esse requisito cria um possível problema: se um app com vários serviços em primeiro plano não gerenciar cuidadosamente a notificação para que ela seja compartilhada em todos os serviços, poderá haver várias notificações persistentes e não dispensáveis, o que vai gerar uma confusão indesejada na lista ativa de notificações.

Esse problema se torna mais desafiador quando você usa SDKs como o Navigation do SDK, que executam serviços em primeiro plano independentemente do app que tem as próprias notificações persistentes independentes, o que dificulta a consolidação delas. Para resolver esses problemas, o SDK de navegação v1.11 introduziu uma API simples para ajudar a gerenciar notificações persistentes em todo o app, inclusive no SDK.

Consolidar notificações persistentes

Componentes

O gerenciador de serviços em primeiro plano fornece um wrapper em torno da classe de serviço em primeiro plano do Android e da classe de notificação persistente. A função principal desse wrapper é forçar a reutilização do ID de notificação para que ela seja compartilhada em todos os serviços em primeiro plano usando o gerenciador.


O SDK do Navigation contém métodos estáticos para inicializar e receber a Singleton de ForegroundServiceManager. Esse singleton só pode ser inicializado uma vez durante a vida útil do SDK do Navigation. Consequentemente, se você usar uma das chamadas de inicialização (initForegroundServiceManagerMessageAndIntent() ou initForegroundServiceManagerProvider()), é necessário envolver com um bloco try-catch caso esse caminho seja inserido novamente. O SDK do Navigation gera uma exceção de tempo de execução se você chamar um dos métodos mais de uma vez, a menos que você primeiro, apague todas as referências ao ForegroundServiceManager e chame clearForegroundServiceManager() antes de cada chamada seguinte.

Os quatro parâmetros de initForegroundServiceManagerMessageAndIntent() são application, notificationId, defaultMessage e resumeIntent. Se o três parâmetros finais forem nulos, a notificação será o padrão Notificação do SDK do Navigation. Ainda é possível ocultar outros serviços em primeiro plano no app por trás dessa notificação. O parâmetro notificationId especifica o ID da notificação que deve ser usado. Se for nulo, um valor arbitrário será usado. É possível configurá-lo explicitamente para contornar conflitos com outras notificações, como as de outro SDK. O defaultMessage é uma string que aparece quando o sistema não está navegar. O resumeIntent é uma intent que é disparada quando a notificação é clicada. Se o resumeIntent for nulo, os cliques na notificação serão ignorados.

Os três parâmetros de initForegroundServiceManagerProvider() são application, notificationId e notificationProvider. Se o resultado dois parâmetros forem nulos, a notificação será o SDK do Navigation padrão notificação. O parâmetro notificationId especifica o ID da notificação que deve ser usado. Se for nulo, um valor arbitrário será usado. É possível configurá-lo explicitamente para contornar conflitos com outras notificações, como as de outro SDK. Se o notificationProvider estiver definido, o provedor será sempre responsável por gerar a notificação a ser renderizada.

O método getForegroundServiceManager() do SDK Navigation retorna o singleton do gerenciador de serviços em primeiro plano. Se você ainda não tiver gerado uma, ele é equivalente a chamar initForegroundServiceManagerMessageAndIntent() com parâmetros nulos para notificationId, defaultMessage e resumeIntent.

O ForegroundServiceManager tem três métodos simples. Os dois primeiros são para movem um serviço para dentro e para fora do primeiro plano e costumam ser chamadas no serviço que foi criado. O uso desses métodos garante que os serviços sejam associados à notificação persistente compartilhada. O método final, updateNotification(), sinaliza para o gerenciador que a notificação foi alterada e precisa ser renderizada novamente.

Se você precisar de controle total da notificação persistente compartilhada, a API oferece uma interface NotificationContentProvider para definir um provedor de notificação, que contém um único método para receber uma notificação com o conteúdo atual. Ele também fornece uma classe base, que pode ser podem usar para definir o provedor. Um dos componentes principais da classe de base é que ele fornece uma maneira de chamar updateNotification() sem o precisa acessar ForegroundServiceManager. Se você usar uma instância do provedor de notificações para receber novas mensagens de notificação, poderá chamar esse método interno diretamente para renderizar a mensagem na notificação.

cenários de uso

Esta seção detalha os cenários de uso para notificações persistentes compartilhadas.

Ocultar notificações persistentes de outros serviços em primeiro plano do app
O cenário mais fácil é preservar o comportamento atual e usar apenas a notificação persistente para renderizar informações do SDK de navegação. Outros serviços pode ficar oculta por trás dessa notificação usando o gerenciador de serviços em primeiro plano métodos startForeground() e stopForeground().
Ocultar notificações persistentes de outros serviços em primeiro plano do app, mas definir texto padrão mostrado quando não está navegando
O segundo cenário mais fácil é preservar o comportamento atual e usar apenas a notificação persistente para renderizar informações do SDK de navegação, exceto quando o sistema não está navegando. Quando o sistema não está navegando, o string fornecida para initForegroundServiceManagerMessageAndIntent() é exibido em vez da string padrão do SDK de navegação que menciona "Google Maps". Você também pode usar essa chamada para definir a intent de currículo que é disparada quando a notificação é clicada.
Assumir o controle total da renderização da notificação persistente
O cenário final exige a definição e a criação de um provedor de notificações e passá-lo para o ForegroundServiceManager usando initForegroundServiceManagerProvider() Essa opção oferece controle total sobre o que é renderizado na notificação, mas também desconecta as informações de notificação do SDK do Navigation do de modo automático, removendo assim as instruções passo a passo úteis mostradas na notificação. O Google não oferece uma maneira simples de recuperar essas informações e inseri-las na notificação.

Exemplo de provedor de notificação

O exemplo de código abaixo demonstra como criar e retornar notificações usando um provedor de conteúdo de notificação simples.

public class NotificationContentProviderImpl
   extends NotificationContentProviderBase
   implements NotificationContentProvider {
 private String channelId;
 private Context context;
 private String message;

 /** Constructor */
 public NotificationContentProviderImpl(Application application) {
   super(application);
   message = "-- uninitialized --";
   channelId = null;
   this.context = application;
 }

 /**
  * Sets message to display in the notification. Calls updateNotification
  * to display the message immediately.
  *
  * @param msg The message to display in the notification.
  */
 public void setMessage(String msg) {
   message = msg;
   updateNotification();
 }

 /**
  * Returns the notification as it should be rendered.
  */
 @Override
 public Notification getNotification() {
   Notification notification;

   if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
     Spanned styledText = Html.fromHtml(message, FROM_HTML_MODE_LEGACY);
     String channelId = getChannelId(context);
     notification =
         new Notification.Builder(context, channelId)
             .setContentTitle("Notifications Demo")
             .setStyle(new Notification.BigTextStyle()
                 .bigText(styledText))
             .setSmallIcon(R.drawable.ic_navigation_white_24dp)
             .setTicker("ticker text")
             .build();
   } else {
     notification = new Notification.Builder(context)
         .setContentTitle("Notification Demo")
         .setContentText("testing non-O text")
         .build();
   }

   return notification;
 }

 // Helper to set up a channel ID.
 private String getChannelId(Context context) {
   if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
     if (channelId == null) {
       NotificationManager notificationManager =
           (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
       NotificationChannel channel = new NotificationChannel(
           "default", "navigation", NotificationManager.IMPORTANCE_DEFAULT);
       channel.setDescription("For navigation persistent notification.");
       notificationManager.createNotificationChannel(channel);
       channelId = channel.getId();
     }
     return channelId;
   } else {
     return "";
   }
 }
}

Depois de criar NotificationContentProviderImpl, conecte o SDK Navigation a ele usando o seguinte código:

ForegroundServiceManager f = NavigationApi.getForegroundServiceManager(getApplication());
mNotification = new NotificationContentProviderImpl(getApplication());
NavigationApi.clearForegroundServiceManager();
NavigationApi.initForegroundServiceManagerProvider(getApplication(), null, mNotification);

Advertências e planos futuros

  • Ligue para initForegroundServiceManagerMessageAndIntent() ou initForegroundServiceManagerProvider() com antecedência para que o cenário de uso esperado esteja bem definido. É preciso chamar esse método antes de criar um novo navegador.
  • Certifique-se de capturar exceções de chamadas para initForegroundServiceManagerMessageAndIntent() ou initForegroundServiceManagerProvider() caso o caminho do código seja inserido mais de uma vez. No Navigation SDK v2.0, chamar esse método várias vezes gera uma exceção verificada em vez de uma exceção de execução.
  • O Google ainda precisa trabalhar para ter um estilo consistente ao longo da vida da notificação que corresponda ao estilo do cabeçalho.
  • Ao definir um provedor de notificação, é possível controlar o comportamento de aviso com a prioridade.
  • O Google não oferece uma maneira simples de recuperar informações detalhadas que um provedor de notificações pode inserir na notificação.