Начиная с 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 указывает идентификатор уведомления, который должен использоваться. Если он равен null, используется произвольное значение. Его можно установить явно, чтобы избежать конфликтов с другими уведомлениями, например, из другого SDK. defaultMessage — это строка, которая отображается, когда система не находится в режиме навигации. resumeIntent — это намерение, которое срабатывает при щелчке по уведомлению. Если resumeIntent равен null, то щелчки по уведомлению игнорируются.
Три параметра функции initForegroundServiceManagerProvider() — это application , notificationId и notificationProvider . Если последние два параметра равны null, то уведомление является стандартным уведомлением Navigation SDK. Параметр notificationId указывает идентификатор уведомления, который должен использоваться для уведомления. Если он равен null, то используется произвольное значение. Вы можете установить его явно, чтобы избежать конфликтов с другими уведомлениями, например, из другого 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 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()на раннем этапе, чтобы четко определить ожидаемый сценарий использования. Этот метод необходимо вызвать перед созданием нового навигатора. - Обязательно перехватывайте исключения, возникающие при вызове методов
initForegroundServiceManagerMessageAndIntent()илиinitForegroundServiceManagerProvider()если соответствующий путь выполнения кода используется более одного раза. В Navigation SDK версии 2.0 многократный вызов этого метода приводит к генерации проверяемого исключения, а не исключения времени выполнения. - Google, возможно, еще предстоит поработать над тем, чтобы добиться единообразного оформления уведомления на протяжении всего его времени, соответствующего стилю заголовка.
- При определении поставщика уведомлений вы можете управлять поведением системы оповещений, устанавливая для этого приоритет.
- Google не предоставляет простого способа получения пошаговой информации, которую поставщик уведомлений мог бы вставить в уведомление.