从 Android API 级别 26 开始, 前台服务。此要求旨在防止您隐藏可能对系统资源(尤其是电池)造成过多需求的服务。这项要求会导致一个潜在问题:如果具有多个前台服务的应用未妥善管理通知,以便在所有服务中共享该通知,则可能会有多个无法关闭的永久性通知,导致活跃通知列表中出现不必要的杂乱。
当您使用 Navigation 等 SDK
SDK,它们独立于应用本身运行前台服务,
单独的持续性通知,这使得它们难以整合。
为了解决这些问题,Navigation SDK v1.11 引入了一个简单的 API,
帮助管理整个应用中的永久通知,包括在 SDK 内显示的通知。
组件
前台服务管理器会为 Android 前台服务类和持久性通知类提供封装容器。此封装容器的 功能是强制重复使用通知 ID, 使用该管理器在所有前台服务之间共享。
Navigation SDK 包含用于初始化和获取 ForegroundServiceManager
单例的静态方法。此单例在 Navigation SDK 的生命周期内只能初始化一次。因此,如果您使用其中一个初始化调用(initForegroundServiceManagerMessageAndIntent()
或 initForegroundServiceManagerProvider()
),则应将其用 try-catch 块括起来,以防重新进入该路径。如果您多次调用任一方法,Navigation SDK 会抛出运行时异常,除非您先清除对 ForegroundServiceManager
的所有引用,并在每次后续调用之前调用 clearForegroundServiceManager()
。
initForegroundServiceManagerMessageAndIntent()
的四个参数为
application
、notificationId
、defaultMessage
和resumeIntent
。如果最后三个参数为 null,则通知是标准的 Navigation SDK 通知。仍然可以隐藏其他前景
。notificationId
参数用于指定应用于通知的通知 ID。如果该值为 null,则使用任意值。您可以明确将其设置为在
与其他 SDK 的通知(如来自其他 SDK 的通知)冲突。defaultMessage
是系统未导航时显示的字符串。resumeIntent
是在用户点击通知时触发的 intent。如果 resumeIntent
为 null,系统会忽略对通知的点击。
initForegroundServiceManagerProvider()
的三个参数为
application
、notificationId
和notificationProvider
。如果最后两个参数为 null,则通知是标准的 Navigation SDK 通知。notificationId
参数指定应用于通知的通知 ID。如果该值为 null,则任意值
。您可以明确设置该参数以解决与其他
通知,如来自其他 SDK 的通知。如果设置了 notificationProvider
,则提供程序始终负责生成要呈现的通知。
Navigation SDK getForegroundServiceManager()
方法会返回前台服务管理器单例。如果您尚未生成代码,则
相当于调用 initForegroundServiceManagerMessageAndIntent()
为 notificationId
、defaultMessage
和
resumeIntent
。
ForegroundServiceManager
有三个简单的方法。前两个用于将服务移入和移出前台,通常从已创建的服务内调用。使用这些方法可确保
与共享的常驻通知相关联。最后一个
方法 updateNotification()
,用于标记通知具有的管理器
已更改,应重新呈现。
如果您需要完全控制共享的常驻通知,
该 API 会提供 NotificationContentProvider
接口,用于定义
通知提供程序,其中包含用于接收通知的单一方法
与当前内容相关联。它还提供了一个基类,您可以选择使用该基类来帮助定义提供程序。基类的主要用途之一是提供一种无需访问 ForegroundServiceManager
即可调用 updateNotification()
的方法。如果您使用
通知提供程序以接收新通知消息,您可以调用此
内部方法直接呈现消息在通知中。
使用场景
本部分详细介绍了使用共享持久性通知的使用场景。
- 隐藏其他应用前台服务的常驻通知
- 最简单的方案是保留当前行为,并且仅使用
用于呈现 Navigation SDK 信息的常驻通知。其他服务
可以使用前台服务管理器隐藏此通知
startForeground()
和stopForeground()
方法。 - 隐藏其他应用前台服务的常驻通知,但已设置 不浏览时显示的默认文字
- 第二种最简单的场景是保留当前行为,仅使用
用于呈现 Navigation SDK 信息的常驻通知,但以下情况除外:
。当系统未导航时,系统会显示提供给
initForegroundServiceManagerMessageAndIntent()
的字符串,而不是提及“Google 地图”的默认 Navigation SDK 字符串。您还可以使用此调用来设置在用户点击通知时触发的 resume intent。 - 完全控制永久性通知的呈现
- 最后一个场景需要定义并创建通知提供程序
并使用以下代码将其传递给
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 v2.0 中,多次调用此方法会抛出检查异常,而不是运行时异常。 - Google 可能仍需要努力,才能在通知的整个生命周期内实现与标题样式一致的样式。
- 定义通知提供程序时,您可以使用优先级来控制动作条行为。
- Google 没有提供一种简单的方法用于检索精细导航信息 通知提供程序可能在通知中插入的信息。