A partir del nivel de API 26 de Android, se requieren notificaciones persistentes para los servicios en primer plano. Este requisito tiene como objetivo evitar que ocultes servicios que puedan exigir excesivamente los recursos del sistema, incluida la batería en particular. Este requisito genera un problema potencial: si una app con varios servicios en primer plano no administra con cuidado la notificación para que se comparta en todos los servicios, puede haber varias notificaciones persistentes que no se pueden descartar, lo que genera un desorden no deseado en la lista activa de notificaciones.
Este problema se vuelve más difícil cuando usas SDKs, como el SDK de Navigation, que ejecutan servicios en primer plano independientes de la app que tienen sus propias notificaciones persistentes independientes, lo que dificulta su consolidación.
Para abordar estos problemas, el SDK de Navigation v1.11 introdujo una API simple que ayuda a administrar las notificaciones persistentes en toda la app, incluso dentro del SDK.

Componentes
El administrador de servicios en primer plano proporciona un wrapper alrededor de la clase de servicio en primer plano de Android y la clase de notificación persistente. La función principal de este wrapper es aplicar la reutilización del ID de notificación para que la notificación se comparta en todos los servicios en primer plano que usen el administrador.

El SDK de Navigation contiene métodos estáticos para inicializar y obtener el singleton ForegroundServiceManager. Este singleton solo se puede inicializar una vez durante el ciclo de vida del SDK de Navigation. Por lo tanto, si usas una de las llamadas de inicialización (initForegroundServiceManagerMessageAndIntent() o initForegroundServiceManagerProvider()), debes rodearla con un bloque try-catch en caso de que se vuelva a ingresar a esa ruta de acceso. El SDK de Navigation arroja una excepción de tiempo de ejecución si llamas a cualquiera de los métodos más de una vez, a menos que primero borres todas las referencias a ForegroundServiceManager y llames a clearForegroundServiceManager() antes de cada llamada posterior.
Los cuatro parámetros de initForegroundServiceManagerMessageAndIntent() son
application, notificationId, defaultMessage y resumeIntent. Si los tres parámetros finales son nulos, la notificación es la estándar del SDK de Navigation. Aún es posible ocultar otros servicios en primer plano en la app detrás de esta notificación. El parámetro notificationId
especifica el ID de notificación que se debe usar para la notificación. Si es nulo, se usa un valor arbitrario. Puedes configurarlo de forma explícita para solucionar conflictos con otras notificaciones, como las de otro SDK. defaultMessage es una cadena que se muestra cuando el sistema no está navegando. El resumeIntent es un intent que se activa cuando se hace clic en la notificación. Si el resumeIntent es nulo, se ignoran los clics en la notificación.
Los tres parámetros de initForegroundServiceManagerProvider() son application, notificationId y notificationProvider. Si los dos últimos parámetros son nulos, la notificación es la estándar del SDK de Navigation. El parámetro notificationId especifica el ID de notificación que se debe usar para la notificación. Si es nulo, se usa un valor arbitrario. Puedes configurarlo de forma explícita para solucionar conflictos con otras
notificaciones, como las de otro SDK. Si se establece notificationProvider, el proveedor siempre es responsable de generar la notificación que se renderizará.
El método getForegroundServiceManager() del SDK de Navigation devuelve el singleton del administrador de servicios en primer plano. Si aún no generaste uno,
equivale a llamar a initForegroundServiceManagerMessageAndIntent()
con parámetros nulos para notificationId, defaultMessage y
resumeIntent.
La clase ForegroundServiceManager tiene tres métodos simples. Los dos primeros sirven para mover un servicio al primer plano y fuera de él, y, por lo general, se llaman desde el servicio que se creó. El uso de estos métodos garantiza que los servicios se asocien con la notificación persistente compartida. El método final, updateNotification(), marca el administrador que cambió la notificación y se debe volver a renderizar.
Si necesitas un control completo de la notificación persistente compartida, la API proporciona una interfaz NotificationContentProvider para definir un proveedor de notificaciones, que contiene un solo método para obtener una notificación con el contenido actual. También proporciona una clase base, que puedes usar de forma opcional para definir el proveedor. Uno de los propósitos principales de la clase base es que proporciona una forma de llamar a updateNotification() sin necesidad de acceder a ForegroundServiceManager. Si usas una instancia del
proveedor de notificaciones para recibir mensajes de notificación nuevos, puedes llamar a este
método interno directamente para renderizar el mensaje en la notificación.
situaciones de uso
En esta sección, se detallan las situaciones de uso de las notificaciones persistentes compartidas.
- Ocultar las notificaciones persistentes de otros servicios en primer plano de la app
- La situación más sencilla es conservar el comportamiento actual y usar solo la notificación persistente para renderizar la información del SDK de Navigation. Otros servicios
pueden ocultarse detrás de esta notificación con los métodos
startForeground()ystopForeground()del administrador de servicios en primer plano. - Oculta las notificaciones persistentes de otros servicios en primer plano de la app, pero establece el texto predeterminado que se muestra cuando no se navega.
- La segunda situación más sencilla es conservar el comportamiento actual y usar solo la notificación persistente para renderizar la información del SDK de Navigation, excepto cuando el sistema no esté navegando. Cuando el sistema no está navegando, se muestra la cadena proporcionada a
initForegroundServiceManagerMessageAndIntent()en lugar de la cadena predeterminada del SDK de Navigation que menciona "Google Maps". También puedes usar esta llamada para establecer el intent de reanudación que se activa cuando se hace clic en la notificación. - Toma el control total de la renderización de la notificación persistente
- La situación final requiere definir y crear un proveedor de notificaciones
y pasarlo a
ForegroundServiceManagerconinitForegroundServiceManagerProvider(). Esta opción te brinda un control total de lo que se renderiza en la notificación, pero también desconecta la información de la notificación del SDK de Navigation de la notificación, lo que quita las indicaciones paso a paso útiles que se muestran en la notificación. Google no proporciona un medio simple para recuperar esta información y, luego, insertarla en la notificación.
Proveedor de notificaciones de ejemplo
En el siguiente ejemplo de código, se muestra cómo crear y devolver notificaciones con un proveedor de contenido de notificaciones simple.
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 "";
}
}
}
Después de crear NotificationContentProviderImpl, conéctale el SDK de Navigation con el siguiente código:
ForegroundServiceManager f = NavigationApi.getForegroundServiceManager(getApplication());
mNotification = new NotificationContentProviderImpl(getApplication());
NavigationApi.clearForegroundServiceManager();
NavigationApi.initForegroundServiceManagerProvider(getApplication(), null, mNotification);
Advertencias y planes futuros
- Asegúrate de llamar a
initForegroundServiceManagerMessageAndIntent()oinitForegroundServiceManagerProvider()con anticipación para que el caso de uso esperado esté bien definido. Debes llamar a este método antes de crear un Navigator nuevo. - Asegúrate de detectar excepciones de las llamadas a
initForegroundServiceManagerMessageAndIntent()oinitForegroundServiceManagerProvider()en caso de que se ingrese a la ruta de acceso del código más de una vez. En el SDK de Navigation v2.0, llamar a este método varias veces arroja una excepción verificada en lugar de una excepción de tiempo de ejecución. - Es posible que Google aún tenga trabajo por hacer para obtener un diseño coherente durante el ciclo de vida de la notificación que coincida con el diseño del encabezado.
- Cuando defines un proveedor de notificaciones, puedes controlar el comportamiento de las notificaciones de aviso con la prioridad.
- Google no proporciona un medio sencillo para recuperar la información paso a paso que un proveedor de notificaciones podría insertar en la notificación.