Объединение мобильных уведомлений

Начиная с уровня Android API 26, для служб переднего плана требуются постоянные уведомления. Это требование призвано помешать вам скрыть службы, которые могут предъявлять чрезмерные требования к системным ресурсам, включая, в частности, батарею. Это требование создает потенциальную проблему: если приложение с несколькими службами переднего плана не управляет уведомлением тщательно, чтобы оно было общим для всех служб, тогда может возникнуть несколько постоянных уведомлений, которые нельзя отклонить, что приведет к нежелательному беспорядку в активном списке уведомлений.

Эта проблема становится более сложной, когда вы используете SDK, такие как Navigation SDK, которые запускают службы приоритета независимо от приложения и имеют свои собственные независимые постоянные уведомления, что затрудняет их консолидацию. Для решения этих проблем в Navigation SDK версии 1.11 представлен простой API, помогающий управлять постоянными уведомлениями в приложении, в том числе внутри SDK.

Объединение постоянных уведомлений

Компоненты

Диспетчер служб переднего плана предоставляет оболочку для класса службы переднего плана Android и класса постоянных уведомлений. Основная функция этой оболочки — обеспечить повторное использование идентификатора уведомления, чтобы уведомление распространялось среди всех служб переднего плана, использующих диспетчер.


Пакет SDK навигации содержит статические методы для инициализации и получения синглтона ForegroundServiceManager . Этот синглтон можно инициализировать только один раз за время существования навигационного SDK. Следовательно, если вы используете один из вызовов инициализации ( initForegroundServiceManagerMessageAndIntent() или initForegroundServiceManagerProvider() ), вам следует окружить его блоком try-catch на случай повторного ввода пути. Навигационный SDK выдает исключение во время выполнения, если вы вызываете любой метод более одного раза, если только вы сначала не очистите все ссылки на ForegroundServiceManager и не вызовете clearForegroundServiceManager() перед каждым последующим вызовом.

Четыре параметра initForegroundServiceManagerMessageAndIntent() — это application , notificationId , defaultMessage и resumeIntent . Если последние три параметра имеют значение NULL, то уведомление является стандартным уведомлением Navigation SDK. За этим уведомлением по-прежнему можно скрыть другие службы переднего плана в приложении. Параметр notificationId указывает идентификатор уведомления, который следует использовать для уведомления. Если оно равно нулю, то используется произвольное значение. Вы можете явно настроить его для устранения конфликтов с другими уведомлениями, например, из другого SDK. defaultMessage — это строка, которая отображается, когда система не осуществляет навигацию. resumeIntent — это намерение, которое активируется при нажатии на уведомление. Если resumeIntent имеет значение NULL, клики по уведомлению игнорируются.

Три параметра initForegroundServiceManagerProvider() — это application , notificationId и notificationProvider . Если последние два параметра имеют значение null, то уведомление является стандартным уведомлением Navigation SDK. Параметр notificationId указывает идентификатор уведомления, который следует использовать для уведомления. Если оно равно нулю, то используется произвольное значение. Вы можете явно настроить его для устранения конфликтов с другими уведомлениями, например, из другого SDK. Если notificationProvider установлен, то поставщик всегда несет ответственность за создание уведомления, которое должно быть отображено.

Метод getForegroundServiceManager() пакета Navigation SDK возвращает одноэлементный элемент диспетчера служб переднего плана. Если вы еще не создали его, это эквивалентно вызову initForegroundServiceManagerMessageAndIntent() с нулевыми параметрами для notificationId , defaultMessage и resumeIntent .

ForegroundServiceManager имеет три простых метода. Первые два предназначены для перемещения службы на передний план и обратно и обычно вызываются из созданной службы. Использование этих методов гарантирует, что службы будут связаны с общим постоянным уведомлением. Последний метод updateNotification() сообщает менеджеру, что уведомление изменилось и его необходимо повторно отобразить.

Если вам нужен полный контроль над общим постоянным уведомлением, API предоставляет интерфейс NotificationContentProvider для определения поставщика уведомлений, который содержит единственный метод для получения уведомления с текущим содержимым. Он также предоставляет базовый класс, который вы можете использовать для определения поставщика. Одна из основных целей базового класса заключается в том, что он предоставляет способ вызова updateNotification() без необходимости доступа к ForegroundServiceManager . Если вы используете экземпляр поставщика уведомлений для получения новых сообщений уведомлений, вы можете вызвать этот внутренний метод напрямую, чтобы отобразить сообщение в уведомлении.

Сценарии использования

В этом разделе подробно описаны сценарии использования общих постоянных уведомлений.

Скрыть постоянные уведомления других служб переднего плана приложения
Самый простой сценарий — сохранить текущее поведение и использовать только постоянное уведомление для отображения информации Navigation SDK. Другие службы могут скрываться за этим уведомлением, используя методы диспетчера служб переднего плана startForeground() и stopForeground() .
Скрыть постоянные уведомления других служб переднего плана приложения, но установить текст по умолчанию, отображаемый при отсутствии навигации.
Второй самый простой сценарий — сохранить текущее поведение и использовать постоянное уведомление только для отображения информации Navigation SDK, за исключением случаев, когда система не осуществляет навигацию. Когда система не выполняет навигацию, отображается строка, предоставленная initForegroundServiceManagerMessageAndIntent() а не строка Navigation SDK по умолчанию, в которой упоминается «Карты Google». Вы также можете использовать этот вызов, чтобы установить намерение возобновления, которое срабатывает при нажатии на уведомление.
Получите полный контроль над отображением постоянного уведомления.
Последний сценарий требует определения и создания поставщика уведомлений и передачи его в ForegroundServiceManager с помощью initForegroundServiceManagerProvider() . Этот параметр дает вам полный контроль над тем, что отображается в уведомлении, но он также отключает информацию уведомления Navigation SDK от уведомления, тем самым удаляя полезные пошаговые подсказки, отображаемые в уведомлении. Google не предоставляет простых средств для получения этой информации и вставки ее в уведомление.

Пример поставщика уведомлений

В следующем примере кода показано, как создавать и возвращать уведомления с помощью простого поставщика содержимого уведомлений.

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 "";
   }
 }
}

После создания NotificationContentProviderImpl вы подключаете к нему навигационный SDK, используя следующий код:

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

Предостережения и планы на будущее

  • Обязательно вызовите initForegroundServiceManagerMessageAndIntent() или initForegroundServiceManagerProvider() заранее, чтобы четко определить ожидаемый сценарий использования. Вы должны вызвать этот метод перед созданием нового навигатора.
  • Обязательно перехватывайте исключения из вызовов initForegroundServiceManagerMessageAndIntent() или initForegroundServiceManagerProvider() если путь кода вводится более одного раза. В Navigation SDK версии 2.0 при многократном вызове этого метода создается проверенное исключение, а не исключение времени выполнения.
  • Google, возможно, еще предстоит поработать над тем, чтобы обеспечить единообразный стиль на протяжении всего срока действия уведомления, соответствующий стилю заголовка.
  • Когда вы определяете поставщика уведомлений, вы можете управлять поведением хедз-ап с приоритетом.
  • Google не предоставляет простых средств для получения пошаговой информации, которую поставщик уведомлений может вставить в уведомление.