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

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

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

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

Компоненты

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


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

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

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

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

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

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

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

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

Скрыть постоянные уведомления других активных служб приложения
Самый простой сценарий — сохранить текущее поведение и использовать постоянное уведомление только для отображения информации Navigation SDK. Другие службы могут скрыться за этим уведомлением, используя методы startForeground() и stopForeground() диспетчера служб переднего плана.
Скрыть постоянные уведомления других активных служб приложения, но установить текст по умолчанию, отображаемый при отсутствии навигации
Второй самый простой сценарий — сохранить текущее поведение и использовать постоянное уведомление только для отображения информации Navigation SDK, за исключением случаев, когда система не осуществляет навигацию. Когда система не осуществляет навигацию, отображается строка, предоставленная initForegroundServiceManagerMessageAndIntent() , а не строка Navigation SDK по умолчанию, в которой упоминается «Google Maps». Вы также можете использовать этот вызов для установки намерения возобновления, которое срабатывает при нажатии на уведомление.
Получите полный контроль над отображением постоянного уведомления.
В последнем сценарии требуется определить и создать поставщик уведомлений и передать его в 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 вы подключаете к нему Navigation SDK с помощью следующего кода:

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

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

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