Notificaciones persistentes compartidas

A partir del nivel de API 26 de Android, se requieren notificaciones persistentes para los servicios en primer plano. El objetivo de este requisito es evitar que ocultes servicios que podrían generar demandas excesivas en los recursos del sistema, incluida la batería en particular. Este requisito crea un posible problema: si una app con varios servicios en primer plano no administra cuidadosamente la notificación para que se comparta en todos los servicios, habrá varias notificaciones persistentes que no se pueden descartar, lo que generará 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 pueden tener sus propias notificaciones persistentes independientes, lo que dificulta la consolidación. Para solucionar estos problemas, la versión 1.11 del SDK de Navigation introdujo una API simple que ayuda a administrar notificaciones persistentes en toda la app, incluido dentro del SDK.

Cómo consolidar notificaciones persistentes

Componentes

El administrador de servicios en primer plano proporciona un wrapper para 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 usan el administrador.


NavigationAPI contiene métodos estáticos para inicializar y obtener el singleton ForegroundServiceManager. Este singleton solo se puede inicializar una vez durante la vida útil del SDK de Navigation. En consecuencia, si usas una de las llamadas de inicialización (initForegroundServiceManagerMessageAndIntent() o initForegroundServiceManagerProvider()), debes encerrarla en un bloque try/catch en caso de que se vuelva a ingresar esa ruta. Para evitar problemas de incompatibilidad, 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. En la versión 2.0 del SDK de Navigation, se agrega una excepción verificada a la API para este fin.

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 notificación estándar del SDK de Navigation. De todos modos, 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 utilizar para la notificación. Si es nulo, se usa un valor arbitrario. Puedes configurarla de manera explícita para solucionar conflictos con otras notificaciones, como las de otro SDK. El defaultMessage es una cadena que se muestra cuando el sistema no está navegando. resumeIntent es un intent que se activa cuando se hace clic en la notificación. Si resumeIntent es nulo, se ignorarán los clics en la notificación.

Los tres parámetros de initForegroundServiceManagerProvider() son application, notificationId y notificationProvider. Si los dos parámetros finales son nulos, la notificación es la notificación estándar del SDK de Navigation. El parámetro notificationId especifica el ID de notificación que se debe utilizar para la notificación. Si es nulo, se usa un valor arbitrario. Puedes configurarla de manera explícita para solucionar conflictos con otras notificaciones, como las de otro SDK. Si se configura notificationProvider, el proveedor siempre es responsable de generar la notificación que se renderizará.

El método getForegroundServiceManager() del SDK de Navigation muestra 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.

ForegroundServiceManager tiene tres métodos simples. Las dos primeras sirven para poner un servicio en primer plano y quitarlo 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 al administrador que la notificación cambió y debe volver a renderizarse.

Si deseas un control completo del contenido de la notificación persistente compartida, la nueva 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 ayudar a definir el proveedor. Uno de los principales propósitos de la clase base es que proporciona un medio fácil para llamar a updateNotification() sin necesidad de acceder a ForegroundServiceManager. Este método auxiliar puede resultar útil si usas una instancia del proveedor de notificaciones para recibir mensajes de notificación nuevos, en cuyo caso puedes llamar directamente a este método interno para renderizar el mensaje en la notificación.

situaciones de uso

En esta sección, se detallan las situaciones de uso para usar notificaciones persistentes compartidas.

Ocultar notificaciones persistentes de otros servicios en primer plano de la app
La situación más fácil es conservar el comportamiento actual y solo usar 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() y stopForeground() del administrador de servicios en primer plano.
Se ocultan las notificaciones persistentes de otros servicios en primer plano de la app, pero se configura el texto predeterminado que se muestra cuando no se navega.
La segunda situación más fácil es conservar el comportamiento actual y solo usar la notificación persistente para renderizar 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". Esta llamada también se puede usar para establecer el intent de reanudación que se activará cuando se haga 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 ForegroundServiceManager a través de initForegroundServiceManagerProvider(). Esta opción te brinda el 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 los mensajes útiles paso a paso que se muestran en la notificación. Google aún no proporciona un medio simple para recuperar esta información y, luego, insertarla en la notificación.

Ejemplo de proveedor de notificaciones

En el siguiente ejemplo de código, se muestra cómo crear y mostrar notificaciones usando 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 "";
   }
 }
}

Advertencias y planes futuros

  • Asegúrate de llamar a initForegroundServiceManagerMessageAndIntent() o initForegroundServiceManagerProvider() con anticipación para que la situación de uso esperada quede bien definida. Debes llamar a este método antes de crear un nuevo navegador.
  • Asegúrate de detectar excepciones de llamadas a initForegroundServiceManagerMessageAndIntent() o initForegroundServiceManagerProvider() en caso de que la ruta de acceso del código se ingrese más de una vez. En el SDK de Navigation versión 2.0, llamar a este método varias veces arroja una excepción verificada en lugar de una excepción de entorno de ejecución.
  • Es posible que Google aún deba trabajar para obtener un estilo coherente durante la vida útil de la notificación que coincida con el estilo del encabezado.
  • Cuando defines un proveedor de notificaciones, puedes controlar el comportamiento de atención con la prioridad.
  • Google aún no proporciona un medio simple para recuperar información paso a paso que un proveedor de notificaciones podría insertar en la notificación.