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 tem como objetivo impedir que você oculte serviços que possam exigir recursos excessivos do sistema, incluindo a bateria. Esse requisito cria um problema em potencial: 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 que não podem ser dispensadas, o que leva a uma confusão indesejada na lista ativa de notificações.
Esse problema se torna mais difícil quando você usa SDKs, como o Navigation
SDK, que executam serviços em primeiro plano independentemente do app e têm
notificações persistentes independentes, dificultando a consolidação.
Para resolver esses problemas, o SDK Navigation v1.11 introduziu uma API simples para
ajudar a gerenciar notificações persistentes no app, incluindo no SDK.
Componentes
O gerenciador de serviços em primeiro plano fornece um wrapper para a classe de serviço em primeiro plano do Android e a classe de notificação persistente. A principal função desse wrapper é forçar a reutilização do ID de notificação para que a notificação seja compartilhada em todos os serviços em primeiro plano usando o gerenciador.
O SDK Navigation contém métodos estáticos para inicializar e receber o singleton ForegroundServiceManager
. Esse singleton só pode ser inicializado uma vez durante o ciclo de vida do SDK Navigation. Consequentemente, se você usar uma das chamadas de inicialização (initForegroundServiceManagerMessageAndIntent()
ou initForegroundServiceManagerProvider()
), coloque-a em um bloco try-catch caso esse caminho seja reinserido. O SDK Navigation
gera uma exceção de tempo de execução se você chamar qualquer um dos métodos mais de uma vez, a menos que
primeiro limpe todas as referências ao ForegroundServiceManager
e chame
clearForegroundServiceManager()
antes de cada chamada subsequente.
Os quatro parâmetros de initForegroundServiceManagerMessageAndIntent()
são application
, notificationId
, defaultMessage
e resumeIntent
. Se os três parâmetros finais forem nulos, a notificação será a padrão do SDK 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. Você pode definir explicitamente para evitar conflitos com outras notificações, como as de outro SDK. O
defaultMessage
é uma string exibida quando o sistema não está
navegando. O resumeIntent
é um intent que é disparado quando a notificação
é clicada. Se 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 os dois últimos parâmetros forem nulos, a notificação será a padrão do SDK Navigation. O parâmetro notificationId
especifica o ID que
deve ser usado para a notificação. Se for nulo, um valor arbitrário será usado. É possível definir explicitamente para evitar conflitos com outras notificações, como as de outro SDK. Se o notificationProvider
for
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 gerou uma, é o equivalente a chamar initForegroundServiceManagerMessageAndIntent()
com parâmetros nulos para notificationId
, defaultMessage
e resumeIntent
.
A classe ForegroundServiceManager
tem três métodos simples. Os dois primeiros são para
mover um serviço para dentro e para fora do primeiro plano e geralmente são chamados de
dentro do serviço que foi criado. O uso desses métodos garante que os serviços estejam associados à notificação persistente compartilhada. O método
final, updateNotification()
, sinaliza ao gerenciador que a notificação mudou
e precisa ser renderizada novamente.
Se você precisar de controle total da notificação persistente compartilhada, a API vai fornecer uma interface NotificationContentProvider
para definir um provedor de notificações, que contém um único método para receber uma notificação com o conteúdo atual. Ele também fornece uma classe de base, que você pode
usar opcionalmente para ajudar a definir o provedor. Um dos principais propósitos da classe base é fornecer uma maneira de chamar updateNotification()
sem a necessidade de acessar o ForegroundServiceManager
. Se você usa uma instância do provedor de notificações para receber novas mensagens de notificação, pode 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 de notificações persistentes compartilhadas.
- Ocultar notificações persistentes de outros serviços em primeiro plano de apps
- O cenário mais fácil é preservar o comportamento atual e usar apenas a
notificação persistente para renderizar informações do SDK Navigation. Outros serviços
podem ficar ocultos atrás dessa notificação usando os métodos
startForeground()
estopForeground()
do gerenciador de serviços em primeiro plano. - Ocultar notificações persistentes de outros serviços em primeiro plano de apps, mas definir o texto padrão mostrado quando não há navegação
- O segundo cenário mais fácil é preservar o comportamento atual e usar apenas
a notificação persistente para renderizar informações do SDK Navigation, exceto
quando o sistema não está navegando. Quando o sistema não está navegando, a
string fornecida a
initForegroundServiceManagerMessageAndIntent()
é exibida em vez da string padrão do SDK Navigation que menciona "Google Maps". Você também pode usar essa chamada para definir o intent de retomada que é acionado quando a notificação é clicada. - Assumir o controle total da renderização da notificação persistente
- O cenário final exige definir e criar um provedor de notificações
e transmiti-lo ao
ForegroundServiceManager
usandoinitForegroundServiceManagerProvider()
. 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 Navigation da notificação, removendo os avisos úteis passo a passo mostrados nela. O Google não oferece uma maneira simples de recuperar essas informações e inseri-las na notificação.
Exemplo de provedor de notificações
O exemplo de código a seguir 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);
Ressalvas e planos futuros
- Chame
initForegroundServiceManagerMessageAndIntent()
ouinitForegroundServiceManagerProvider()
no início para que o cenário de uso esperado seja bem definido. É preciso chamar esse método antes de criar um novo navegador. - Não se esqueça de capturar exceções de chamadas para
initForegroundServiceManagerMessageAndIntent()
ouinitForegroundServiceManagerProvider()
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 tempo de execução. - O Google ainda pode ter trabalho a fazer para ter um estilo consistente durante a vida útil da notificação que corresponda ao estilo do cabeçalho.
- Ao definir um provedor de notificações, é possível controlar o comportamento de heads-up com a prioridade.
- O Google não oferece um meio simples de recuperar informações de trajeto que um provedor de notificações pode inserir na notificação.