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 impede que você oculte serviços que possam gerar demandas excessivas em recursos do sistema, incluindo a 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, pode haver várias notificações persistentes não dispensáveis, levando a uma sobrecarga indesejada na lista de notificações ativas.
Esse problema se torna mais desafiador quando você usa SDKs, como o SDK de
navegação, que executam serviços em primeiro plano independentemente do app que tenha as
próprias notificações persistentes e independentes, dificultando a consolidação.
Para resolver esses problemas, o SDK do Navigation v1.11 introduziu uma API simples para
ajudar a gerenciar notificações persistentes em todo o app, incluindo no SDK.
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 principal função desse wrapper é forçar a reutilização do ID da 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 o
Singleton 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()
), será necessário circundá-la
com um bloco try-catch caso esse caminho seja inserido novamente. O SDK do Navigation
gerará uma exceção de execução se você chamar um dos métodos mais de uma vez, a menos que
primeiro apague 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 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 precisa ser usado. Se for nulo, um valor arbitrário será usado. Você pode defini-lo explicitamente para contornar
conflitos com outras notificações, como as de outro SDK. O
defaultMessage
é uma string mostrada quando o sistema não está
navegando. O resumeIntent
é uma intent 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 os dois
parâmetros finais forem nulos, a notificação será a notificação padrão do SDK
do Navigation. O parâmetro notificationId
especifica o ID da notificação que precisa ser usado para a notificação. Se for nulo, um valor arbitrário será usado. Você pode defini-lo explicitamente para contornar 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 que será renderizada.
O método getForegroundServiceManager()
do SDK do Navigation retorna o
Singleton do gerenciador de serviços em primeiro plano. Se você ainda não gerou um,
ele é equivalente a chamar initForegroundServiceManagerMessageAndIntent()
com parâmetros nulos para notificationId
, defaultMessage
e
resumeIntent
.
O ForegroundServiceManager
tem três métodos simples. As duas primeiras servem para mover um serviço para dentro e para fora do primeiro plano e geralmente são chamadas de dentro do 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 o gerenciador de que a notificação mudou e precisa ser renderizada novamente.
Se for necessário ter controle total sobre a notificação persistente compartilhada, a API fornecerá 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 usada para ajudar a definir o provedor. Uma das principais finalidades
da classe de base é oferecer uma maneira de chamar updateNotification()
sem a
necessidade de acessar o ForegroundServiceManager
. Se você usa uma instância do
provedor de notificação para receber novas mensagens, chame 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 usar notificações persistentes compartilhadas.
- Ocultar notificações persistentes de outros serviços de app em primeiro plano
- O cenário mais fácil é preservar o comportamento atual e usar a
notificação persistente apenas para renderizar as informações do SDK de navegação. Outros serviços
podem se ocultar por trá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 estiver navegando
- O segundo cenário mais fácil é preservar o comportamento atual e usar a notificação persistente apenas para renderizar as informações do SDK de navegação, exceto quando o sistema não está navegando. Quando o sistema não está navegando, a string fornecida para
initForegroundServiceManagerMessageAndIntent()
é exibida, em vez da string padrão do SDK de navegação que menciona "Google Maps". Essa chamada também pode ser usada para definir a intent de retomada que é disparada quando a notificação é clicada. - Assumir controle total da renderização da notificação persistente
- O cenário final requer a definição e a criação de um provedor de notificações
e a transmissão dele para o
ForegroundServiceManager
usandoinitForegroundServiceManagerProvider()
. Essa opção oferece controle total sobre o que é renderizado na notificação, mas também desconecta as informações do SDK de navegação da notificação, removendo assim as instruções úteis mostradas na notificação. O Google não oferece um meio simples para recuperar essas informações e inseri-las na notificação.
Exemplo de provedor de notificação
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 o NotificationContentProviderImpl
, conecte o
SDK do 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
- Chame
initForegroundServiceManagerMessageAndIntent()
ouinitForegroundServiceManagerProvider()
com antecedência para que o cenário de uso esperado seja bem definido. É necessário chamar esse método antes de criar um novo Navigator. - Capture exceções de chamadas para
initForegroundServiceManagerMessageAndIntent()
ouinitForegroundServiceManagerProvider()
caso o caminho do código seja inserido mais de uma vez. No SDK do Navigation 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 trabalhar para conseguir um estilo consistente durante o ciclo de vida da notificação que corresponda ao estilo do cabeçalho.
- Ao definir um provedor de notificações, é possível controlar o comportamento dos avisos com a prioridade.
- O Google não oferece um meio simples para recuperar informações de navegação guiada que um provedor de notificações possa inserir na notificação.