此 Codelab 是“使用 Kotlin 进行高级 Android 开发”课程的一部分。如果您按顺序学习这些 Codelab,您将会充分发掘课程的价值,但并不强制要求这样做。“使用 Kotlin 进行高级 Android 开发”Codelab 着陆页列出了所有课程 Codelab。
简介
通知是指在应用界面之外向用户显示的消息。如果设备处于解锁状态,通知会显示在屏幕顶部;如果设备处于锁定状态,通知会显示在锁定屏幕上,具体取决于安全设置。
典型的通知包含标题、说明和图标。通知还可以包含可点击的操作、快速回复、可扩展的内容和图片。
通知可以及时传递内容,并且可以包含按钮,让用户能够执行快速操作,例如发送回复或延后闹钟。点击通知后,用户会进入应用中与通知内容相关的视图。
当应用在后台运行时,通知可用于提醒用户执行重要任务、告知用户发生了某些事情,或传达用户需要立即了解的重要信息。谨慎使用通知。这不仅尊重了用户,还更有可能让您的应用通知获得应有的关注。
在此 Codelab 中,您将学习如何在 Android 应用中创建和使用通知。
您应当已掌握的内容
您应熟悉以下内容:
- 如何使用 Kotlin 创建 Android 应用。尤其是使用 Android SDK。
- 如何使用架构组件和数据绑定来设计应用。
- 对 BroadcastReceiver 有基本的了解
- 对 AlarmManager 有基本的了解
学习内容
- 如何创建、设置样式和发送通知。
- 如何取消通知。
- 如何创建通知渠道。
- 如何向通知添加快捷操作。
- 如何在应用图标上显示通知标记。
您将执行的操作
- 向 starter 应用添加通知。
- 取消您之前发送的通知。
- 为不同类型的通知创建渠道。
- 在初始应用中自定义通知。
- 添加快捷操作,让通知更具互动性。
- 关闭通知标记。
煮鸡蛋很简单,但如果您不注意时间,可能会遇到挑战。在此 Codelab 中,您将开发一个鸡蛋计时器应用,并使其像您未来的鸡蛋一样完美。您将从一个可正常运行的鸡蛋计时器应用开始,该应用可让用户为不同烹饪方式的鸡蛋设置不同的烹饪时间。计时器会从所选时间间隔开始倒计时,并在鸡蛋煮好时显示消息框。
这可能看起来很实用,但远非完美,而且不太方便用户使用。首先,Toast 消息仅显示很短的时间,因此很容易错过。此外,如果应用不在前台或设备已锁定,则在 Toast 消息消失后,计时器的状态不会有任何视觉指示。
理想情况下,计时器应使用通知来告知用户时间到了。用户确实需要立即知道鸡蛋是否煮好,否则鸡蛋就会煮过头! 通知是可视的,可以包含声音,还可以使设备振动,所有这些方式都可以吸引用户的注意力!这样一来,您就可以做出完美的鸡蛋,让用户满意而归。
如需获取示例应用,您可以执行以下任一操作:
从 GitHub 克隆代码库,然后切换到 starter 分支。
$ git clone https://github.com/googlecodelabs/android-kotlin-notifications
或者,您也可以下载 ZIP 文件形式的代码库,将其解压缩并在 Android Studio 中打开。
- 在 Android Studio 中打开并运行应用。
您会看到一个鸡蛋图片和一个下拉菜单,其中列出了预定义的煮蛋时间间隔。点击软煮下拉菜单对应的三角形。列表中的第一个选项用于测试目的,会将闹钟设置为仅响铃 10 秒。列表旁边有一个开关,用于启动计时器。您可以使用此开关随时开始和停止计时。起始代码可完全正常运行,这意味着您可以设置鸡蛋计时器,并观看它倒计时到 0。当计时器结束时,系统会显示一条 Toast 消息,如下所示。
- 检查源代码。起始应用包含一个名为
MainActivity
的 activity。有三个子软件包,名称分别为receiver
、ui
和util
。
- /receiver -
receiver
软件包包含两个名为AlarmReceiver
和SnoozeReceiver
的广播接收器。AlarmReceiver
由AlarmManager
触发,以便在用户定义的计时器到时发送通知。SnoozeReceiver
处理用户点击以暂停通知。 - /ui - 包含
EggTimerFragment
,它是应用界面部分的一部分。EggTimerViewModel
负责启动和取消计时器以及其他与生命周期相关的应用任务。 - /util - 此软件包中有两个文件。
BindingUtils.kt
具有绑定适配器,可在应用界面与ViewModel
之间实现数据绑定。NotificationUtils.kt
具有针对NotificationManager
的扩展方法。
使用通知是吸引用户关注应用的绝佳方式。无论应用是否正在运行或在前台运行,通知都会在屏幕顶部显示一个弹出式窗口,并且可能包含声音或振动。如需创建通知,您需要使用通知构建器并提供标题文字、内容文字和图标。当 builder 包含所有必需字段后,系统服务 NotificationManager
会帮助您将此内容显示为通知。NotificationManager
负责发送通知、更新其内容和取消通知。在接下来的步骤中,您将向 NotificationManager
添加扩展方法。这样,每次需要使用 NotificationManager
时,您都可以使用这些扩展函数来实现所需的功能。
第 1 步:创建基本通知
在此任务中,您将创建新通知、为用户设置消息,并发送通知。
- 打开
NotificationUtils.kt
类并找到TODO: Step 1.1
。您会在本 Codelab 和应用代码中找到匹配的 TODO。 - 检查给定的
sendNotification()
函数。您将扩展此扩展函数,使其能够通过NotificationManager
发送通知。
//NotificationUtils.kt
// TODO: Step 1.1 extension function to send messages (GIVEN)
/**
* Builds and delivers a notification.
*
* @param messageBody, notification text.
* @param context, activity context.
*/
fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
- 获取通知构建器的实例,传入应用上下文和渠道 ID。频道 ID 是频道的字符串值。
通知渠道是一种对通知进行分组的方式。通过将类似类型的通知分组在一起,开发者和用户可以控制渠道中的所有通知。创建渠道后,即可使用该渠道传送任意数量的通知。
//NotificationUtils.kt
// TODO: Step 1.2 get an instance of NotificationCompat.Builder
val builder = NotificationCompat.Builder(
applicationContext,
applicationContext.getString(R.string.egg_notification_channel_id)
)
- 设置通知图标以表示您的应用、标题以及您想向用户显示的消息的内容文本。在此 Codelab 中,您将看到更多用于进一步自定义通知的选项,但这是发送通知所需设置的最少数据量。
//NotificationUtils.kt
// TODO: Step 1.3 set title, text and icon to builder
.setSmallIcon(R.drawable.cooked_egg)
.setContentTitle(applicationContext.getString(R.string.notification_title))
.setContentText(messageBody)
- 接下来,您需要使用通知的唯一 ID 和来自构建器的
Notification
对象来调用notify()
。
此 ID 表示当前通知实例,更新或取消此通知时需要用到。由于您的应用在任意给定时间只会有一条有效通知,因此您可以为所有通知使用同一 ID。为此,您已在 NotificationUtils.kt
中获得一个名为 NOTIFICATION_ID
的常量。请注意,由于您是从同一类中的扩展函数执行调用,因此可以直接调用 notify()
。
//NotificationUtils.kt
// TODO: Step 1.4 call notify to send the notification
// Deliver the notification
notify(NOTIFICATION_ID, builder.build())
- 打开
ui/EggTimerViewModel.kt
并找到startTimer()
函数。当用户启用计时器时,此函数会创建具有所选时间间隔的闹钟。 - 当用户启动计时器时,您将在此函数中触发通知。为了调用您之前实现的
sendNotification()
函数,您需要一个NotificationManager
实例。NotificationManager
是一种系统服务,可提供为通知 API 公开的所有功能,包括您添加的扩展函数。无论何时,只要您想发送、取消或更新通知,都需要向系统请求NotificationManager
的实例。使用通知消息和上下文调用sendNotification()|
函数。
// EggTimerViewModel.kt
// TODO: Step 1.5 get an instance of NotificationManager
// and call sendNotification
val notificationManager = ContextCompat.getSystemService(
app,
NotificationManager::class.java
) as NotificationManager
notificationManager.sendNotification(app.getString(R.string.timer_running), app)
您已经非常接近目标了。不过,如果您现在运行应用并设置计时器,则不会收到通知。
- 打开
logcat
并搜索"No Channel found"
。您应该会看到一条错误消息,指出egg_channel
不存在。在以下步骤中,您将详细了解通知渠道并修复此问题。
第 2 步:通知渠道
从 API 级别 26 开始,所有通知都必须分配到相应的渠道。如果您点按并按住应用启动器图标,选择“应用信息”,然后点按“通知”,您会看到与该应用关联的通知渠道列表。目前,该列表为空,因为您的应用尚未创建任何渠道。
渠道代表一种通知“类型” - 例如,您的鸡蛋计时器可以在鸡蛋煮熟时发送通知,还可以使用另一个渠道发送每日通知,提醒您在早餐时吃鸡蛋。一个渠道中的所有通知都会归为一组,用户可以为整个渠道配置通知设置。这样一来,用户就可以根据自己感兴趣的通知类型来个性化设置通知。例如,用户可以停用早餐通知,但仍选择接收来自计时器的通知。
开发者可以设置要应用于渠道中所有通知的初始设置、重要性和行为。设置初始设置后,用户可以覆盖这些设置。
在第 1.1 步中,您使用了 egg_notification_channel_id
作为通知渠道,因此现在需要实际创建并自定义此渠道的通知设置和行为。
- 打开
EggTimerFragment.kt
并找到createChannel()
函数。 - 将唯一频道 ID 传递给
NotificationChannel
的构造函数。 - 传递通知渠道名称,用户还会在设置界面中看到该名称。
- 作为最后一个参数,传递通知渠道的重要性级别。此 Codelab 稍后将介绍重要性级别,因此您现在可以使用
NotificationManag
er.IMPORTANCE_LOW
。 - 在
notificationChannel
对象上,将enableLights
设置为 true。此设置会在显示通知时启用指示灯。 - 在
notificationChannel
对象中,将lightColor
设置为红色,以便在显示通知时显示红灯。 - 在
notificationChannel
对象上,将enableVibration
设置为 true 以启用振动。 - 在
notificationChannel
对象上,将频道说明设置为‘Time for breakf
ast'。 - 通过调用
getSystemService()
获取NotificationManager
的实例。 - 对
NotificationManager
调用createNotificationChannel()
并传递您在上一步中创建的notificationChannel
对象。
//EggTimerFragment.kt
private fun createChannel(channelId: String, channelName: String) {
// TODO: Step 1.6 START create a channel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel = NotificationChannel(
channelId,
channelName,
// TODO: Step 2.4 change importance
NotificationManager.IMPORTANCE_LOW
)
// TODO: Step 2.6 disable badges for this channel
notificationChannel.enableLights(true)
notificationChannel.lightColor = Color.RED
notificationChannel.enableVibration(true)
notificationChannel.description = "Time for breakfast"
val notificationManager = requireActivity().getSystemService(
NotificationManager::class.java
)
notificationManager.createNotificationChannel(notificationChannel)
}
// TODO: Step 1.6 END create channel
}
- 接下来,如需创建渠道,您需要调用刚刚编写的
createChannel()
函数(步骤 1.7)。此函数接受两个参数,即频道 ID 和频道名称。您需要从项目中已提供的字符串资源中查找频道 ID 和频道名称。
// EggTimerFragment.kt
// TODO: Step 1.7 call createChannel
createChannel(
getString(R.string.egg_notification_channel_id),
getString(R.string.egg_notification_channel_name)
)
- 您需要将渠道 ID 传递给通知构建器。您已在第 1.2 步中完成此操作。如果将错误的渠道 ID 设置为值,通知将会失败。打开
NotificationUtils.kt
,验证您之前设置的频道 ID 是否正确。
// NotificationUtils.kt
val builder = NotificationCompat.Builder(
applicationContext,
// TODO: Step 1.8 verify the notification channel name
applicationContext.getString(R.string.egg_notification_channel_id)
)
- 运行应用,您会看到每次启动计时器时,应用都会发送通知。
- 拉下状态栏,观察通知标题、内容和图标是否与您在上一步中设置的一致。
- 如需验证新创建的频道,请关闭应用并找到应用图标。长按应用图标,然后选择应用信息。
- 从设置列表中选择通知。您应该会在显示通知设置的正下方看到一个名为 Egg 的新渠道。
现在,当您运行应用时,系统会显示通知。作为应用开发者,您可以自定义此渠道上发送的所有通知的设置和行为,您的用户也可以这样做。恭喜,您已创建通知!
第 3 步:向应用添加通知
到目前为止,我们展示了通知 API 的基本用法,但在启动计时器后立即发送通知并没有多大意义。用户可能更希望在蛋孵化完成时收到通知。在此 Codelab 的下一部分中,您将修复此问题并将 Toast 消息更改为通知。
您已发送通知并观察了通知向用户的显示方式,但这只是创建出色通知的第一步。在此步骤中,您将更改通知的发送时间,使其更合适。
您的应用使用 AlarmManager
设置闹钟。与 AlarmManager
相关的代码已在起始代码中给出,并用于显示消息框消息。AlarmManager
会跟踪所需的时间选择,并在时间到达时触发 AlarmReceiver.kt
的 onReceive()
函数。如果您打开 AlarmReceiver.kt
并前往 onReceive()
,您应该会看到每次设置计时器时显示的 Toast 消息。
- 打开
AlarmReceiver.kt
(NotificationManager
的实例),并使用消息文本和上下文参数调用sendNotification()
函数。
// AlarmReceiver.kt
// TODO: Step 1.9 add call to sendNotification
val notificationManager = ContextCompat.getSystemService(
context,
NotificationManager::class.java
) as NotificationManager
notificationManager.sendNotification(
context.getText(R.string.eggs_ready).toString(),
context
)
- (可选)移除 Toast,因为应用会在计时器到时发送通知。
// AlarmReceiver.kt
// TODO: Step 1.10 [Optional] remove toast
// Toast.makeText(
// context,
// context.getText(R.string.eggs_ready),
// Toast.LENGTH_SHORT
// ).show()
- 运行应用。每次启动计时器和每次计时器结束时,您都应该会看到通知。
这并不理想。您不希望向用户发送过多的通知。您可以移除用户开始计时器时发送的第一个通知。
- 打开
EggTimerFragment.kt
并移除第 1.5 步的通知代码。
// EggTimeViewModel.kt
// TODO: Step 1.5 get an instance of NotificationManager
// and call sendNotification
// val notificationManager = ContextCompat.getSystemService(
// app,
// NotificationManager::class.java
// ) as NotificationManager
// notificationManager.sendNotification(app.getString(R.string.eggs_ready), app)
- 再次运行应用。
- 设置计时器,将其置于后台,然后等待时间结束。您会看到一条通知。这是一种更有用的通知。
第 4 步:添加内容 intent
- 再次运行应用(如果该应用尚未运行)。
- 点击此通知。毫无反应!
显示通知并告知用户固然很好,但当用户点击通知时,他们希望返回到相应的应用。在此 Codelab 部分中,您将向通知添加 intent,以便将用户带回计时器界面。
Intent
是一种消息传递对象,可用于向其他应用组件请求操作。Intent 可用于启动 activity、服务或传递广播。在这种情况下,您可以使用此 intent 来告知系统在用户点按通知时打开 MainActivity
。由于您的应用仅包含一个视图,因此您在此处没有太多选项。不过,在较大的应用中,通知应通过将用户带到适合其与通知互动的界面来打造顺畅的体验。
- 打开
NotificationUtils.kt
并找到sendNotification()
扩展函数。 - 使用您的
applicationContext
和要启动的 activityMainActivity::class.java
创建Intent
。
// NotificationUtils.kt
fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
// Create the content intent for the notification, which launches
// this activity
// TODO: Step 1.11 create intent
val contentIntent = Intent(applicationContext, MainActivity::class.java)
您创建了 intent,但通知显示在您的应用之外。若要使 intent 在应用之外正常运行,您需要创建一个新的 PendingIntent
。
PendingIntent
授予其他应用或系统代表您的应用执行操作的权限。PendingIntent
本身只是对系统维护的令牌的引用,该令牌描述了用于检索它的原始数据。这意味着,即使其所属应用的进程被终止,PendingIntent
本身仍可供已获得它的其他进程使用。在这种情况下,无论计时器应用是否正在运行,系统都会使用待处理 intent 代表您打开相应应用。
- 创建包含
applicationContext
、NOTIFICATION_ID
、您在上一步中创建的contentIntent
和PendingIntent
标志的PendingIntent
。PendingIntent
标志用于指定是创建新的PendingIntent
还是使用现有的PendingIntent
。您需要将PendingIntent.FLAG_UPDATE_CURRENT
设置为标志,因为您不希望在已有通知的情况下创建新通知。这样,您将修改与您提供的 intent 相关联的当前PendingIntent
。
// NotificationUtils.kt
// TODO: Step 1.12 create PendingIntent
val contentPendingIntent = PendingIntent.getActivity(
applicationContext,
NOTIFICATION_ID,
contentIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
- 将
PendingIntent
传递给通知。您可以通过对NotificationBuilder
调用setContentIntent()
来执行此操作。现在,当您点击通知时,系统会触发PendingIntent
,打开MainActivity
。 - 同时将
setAutoCancel()
设置为true
,以便在用户点按通知时,通知会自行关闭,同时将用户带到应用中。
// NotificationUtils.kt
// TODO: Step 1.13 set content intent
.setContentIntent(contentPendingIntent)
.setAutoCancel(true)
- 再次运行应用。
- 设置计时器,将应用置于后台,然后等待通知显示。
- 看到通知后,下拉状态栏,然后点击通知,观察应用如何被调入前台。
第 5 步:取消通知
您有一个可正常运行的带通知功能的计时器,但存在一个小问题。如果您设置了计时器,收到通知,然后再次设置计时器,则在新的计时器运行期间,之前的通知会保留在状态栏中。如果应用在后台运行,这可能会让用户感到困惑,并可能导致鸡蛋煮得不够熟。
若要解决此问题,您需要在开始新的计时器时清除之前的通知。首先,在 NotificationUtils.kt
中创建另一个扩展函数。NotificationManager
具有一个用于取消所有有效通知的 API,名为 cancelAll
()
。
- 打开
NotificationsUtil.kt
。 - 在
NotificationManager
上添加一个调用cancelAll()
的扩展函数。
// NotificationUtils.kt
// TODO: Step 1.14 Cancel all notifications
/**
* Cancels all notifications.
*
*/
fun NotificationManager.cancelNotifications() {
cancelAll()
}
- 打开
EggTimerViewModel.kt
,然后前往startTimer()
函数。 - 在
startTimer()
内,从系统获取NotificationManager
的实例并调用cancelNotifications()
。
// EggTimerViewModel.kt
//TODO Step 1.15 call cancel notification
val notificationManager =
ContextCompat.getSystemService(
app,
NotificationManager::class.java
) as NotificationManager
notificationManager.cancelNotifications()
- 运行应用并启动计时器。
- 看到通知后,再次启动计时器,并观察我们的应用如何自动从状态栏中删除之前的通知。
通知框架为开发者提供了多种自定义选项,以便他们根据需要设置自定义操作和样式。在此任务中,您将学习如何自定义煮蛋器通知。
第 1 步:设置通知样式
根据您的需求和通知内容设置通知样式,可让通知更加醒目,看起来更像是应用的扩展。通知框架附带了多种内置样式,可为您提供帮助,您也可以随时创建自己的样式。
NotificationCompat
提供以下内置样式:
BigTextStyle
,可以显示大段文字,例如在展开时显示电子邮件的内容。BigPictureStyle
,显示包含大型图片附件的大格式通知。InboxStyle
,显示对话风格的文本内容。MediaStyle
,用于显示媒体播放控件。MessagingStyle
,可显示包含任意数量的人员之间的多条消息的大格式通知。
如需详细了解其他样式,请参阅创建展开式通知文档。在此步骤中,您将使用 NotificationCompat.BigPictureStyle
创建一个可展开的通知,该通知在展开时会显示一张大鸡蛋图片。
- 打开
NotificationUtils.kt
并找到sendNotification()
函数。 - 首先,使用
BitmapFactory
从resources
加载图片。
// NotificationUtils.kt
// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
applicationContext.resources,
R.drawable.cooked_egg
)
- 创建新的
BigPictureStyle
并设置映像。 - 将
bigLargeIcon()
设置为null
,这样大图标就会在通知展开时消失。
// NotificationUtils.kt
// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
applicationContext.resources,
R.drawable.cooked_egg
)
val bigPicStyle = NotificationCompat.BigPictureStyle()
.bigPicture(eggImage)
.bigLargeIcon(null)
- 将样式设置为
setStyle()
,值为bigPicStyle
。 - 使用
setLargeIcon()
将大图标设置为eggImage
,这样当通知收起时,图片将显示为较小的图标。
// NotificationUtils.kt
// TODO: Step 2.1 add style to builder
.setStyle(bigPicStyle)
.setLargeIcon(eggImage)
- 运行应用并设置计时器。首次显示通知时,通知会以收起状态显示在抽屉式通知栏中。如果您展开通知,扩展的通知区域会显示一张大图片。
第 2 步:通知操作
通知操作是您可以添加到通知中的另一种自定义设置。目前,当用户点击通知时,系统会将他们重定向到您的应用。除了这种默认的通知操作之外,您还可以添加可在通知中完成与应用相关任务的操作按钮。
一个通知最多可以提供三个操作按钮,让用户能够快速响应,例如暂停提醒或回复短信。这些操作按钮不应该重复用户在点按通知时执行的操作。
如需添加操作按钮,请将 PendingIntent
传递给构建器上的 addAction()
函数。这类似于通过调用 setContentIntent()
设置通知的默认点按操作,不同的是不会启动 activity,而是可以完成各种其他任务,例如启动在后台执行作业的 BroadcastReceiver
,这样该操作就不会干扰已经打开的应用。
在此 Codelab 中,您已经获得了一个名为 SnoozeReceiver
的 BoadcastReceiver
。您将使用 SnoozeReceiver
接收用户对通知操作的点击。在以下步骤中,您将添加代码,以便在用户点击“打盹”操作按钮时,将鸡蛋计时器通知设为打盹 60 秒。当用户点击“延后”操作时,SnoozeReceiver
将收到 intent,并创建一个新闹钟,以便在 60 秒后发送新通知。
- 打开
SnoozeReceiver.kt
。此类与您之前使用的AlarmReceiver
类似。在以下步骤中,您将添加代码来触发SnoozeReceiver
的onReceive()
函数。简而言之,SnoozeReceiver
中的代码将创建一个新闹钟,以便在一分钟后发送新通知。向下滚动到 onReceive 函数的底部,从系统获取 notificationManager 的实例,然后调用 cancelAll。
// SnoozeReceiver.kt
val notificationManager = ContextCompat.getSystemService(
context,
NotificationManager::class.java
) as NotificationManager
notificationManager.cancelAll()
- 如需使用
SnoozeReceiver
,请打开NotificationUtils.kt
。 - 在
sendNotification()
函数中的样式之后,立即为SnoozeReceiver
创建新的Intent
snoozeIntent
。 - 通过对
PendingIntent
调用getBroadcast()
方法来创建待处理 intent,该方法需要以下步骤中的参数。系统将使用此PendingIntent
设置一个新闹钟,以便在用户点按“打盹”按钮后 60 秒发布新通知。 - 第一个参数是
PendingIntent
应在其中启动 activity 的应用上下文。 - 第二个参数是请求代码,即相应待处理 intent 的请求代码。如果您需要更新或取消此待处理 intent,则需要使用此代码来访问该待处理 intent。
- 接下来,添加
snoozeIntent
对象,即要启动的 activity 的 intent。 - 最后,添加标志值
#FLAG_ONE_SHOT
,因为 intent 只会使用一次。快速操作和通知会在首次点按后消失,因此意图只能使用一次。
// NotificationUtils.kt
// TODO: Step 2.2 add snooze action
val snoozeIntent = Intent(applicationContext, SnoozeReceiver::class.java)
val snoozePendingIntent: PendingIntent = PendingIntent.getBroadcast(
applicationContext,
REQUEST_CODE,
snoozeIntent,
FLAGS
)
- 接下来,对
notificationBuilder
调用addAction()
函数。此函数需要一个图标和一段文字来向用户说明您的操作。您还需要添加snoozeIntent
。当用户点击您的操作时,系统将使用此 intent 来触发正确的boadcastReceiver
。
// NotificationUtils.kt
// TODO: Step 2.3 add snooze action
.addAction(
R.drawable.egg_icon,
applicationContext.getString(R.string.snooze),
snoozePendingIntent
)
- 运行“鸡蛋计时器”应用以测试“打盹”操作。
- 运行计时器并将应用置于后台。当计时器时间到时,展开通知,您会看到通知现在有一个“暂停”操作按钮,可将鸡蛋计时器暂停一分钟。
第 3 步:通知的重要程度
重要程度决定了通知应在多大程度上干扰用户(视觉上和听觉上)。重要程度越高的通知对用户的干扰程度就越高。
您必须在 NotificationChannel
构造函数中指定重要性级别。您最初为计时器应用设置了较低的重要性。您可以使用从 IMPORTANCE_NONE(0)
到 IMPORTANCE_HIGH(4)
的五个重要性级别之一。您为渠道指定的重要性级别会应用到您在其中发布的所有通知消息。
渠道重要性级别
用户可见的重要性级别 | 重要性(Android 8.0 及更高版本) | 优先级(Android 7.1 及更低版本) |
发出提示音,并以浮动通知的形式显示(在屏幕顶部弹出) | ||
发出提示音 | ||
无声 | ||
不发出提示音,且不会在状态栏中显示 |
如需了解如何选择适当的优先级,请参阅通知设计指南中的“优先级”部分。为应用中的通知选择重要程度时,您应谨慎考虑。选择渠道重要程度时,应考虑用户的时间和注意力。当不重要的通知伪装成紧急通知时,可能会发出不必要的警报,让人分心。用户可以完全控制通知的重要程度,因此如果您创建的通知令人反感,他们可以完全关闭您的通知渠道。
在步骤 1.6 中首次创建通知时,由于该通知旨在不打扰用户,因此将计时器设置为发送低优先级的通知。不过,最好在鸡蛋煮过头之前引起用户的注意。如需更改通知的重要程度级别,请先从渠道设置入手。渠道重要性会影响在渠道中发布的所有通知的干扰级别,因此必须在 NotificationChannel
构造函数中指定渠道重要性。
- 如需更改应用通知渠道的重要性级别,请打开
EggTimerFragment.kt
并前往createChannel()
。将重要程度从IMPORTANCE_LOW
更改为IMPORTANCE_HIGH
。
// EggTimerFragment.kt
val notificationChannel = NotificationChannel(
channelId,
channelName,
// TODO: Step 2.4 change importance
NotificationManager.IMPORTANCE_HIGH
)
如需支持搭载 Android 7.1(API 级别 25)或更低版本的设备,您还必须使用 NotificationCompat
类中的优先级常量针对每条通知调用 setPriority()
。
- 打开
NotificationUtils.kt
,并将以下内容添加到通知构建器对象。
// NotificationUtils.kt
.addAction(
R.drawable.common_google_signin_btn_icon_dark,
applicationContext.getString(R.string.snooze),
snoozePendingIntent
)
// TODO: Step 2.5 set priority
.setPriority(NotificationCompat.PRIORITY_HIGH)
- 在运行应用之前,请在设备或模拟器上长按应用图标,然后选择“卸载”以清除之前的渠道设置。如果您未能成功卸载应用,渠道优先级设置将不会更改,并且在发布通知时也不会出现任何行为变化。
- 现在,再次运行应用并启动计时器。这次,当通知送达时,无论应用是在前台还是后台运行,您都应该会在屏幕顶部看到一个弹出式窗口。
第 4 步:通知标记
通知标志是当关联的应用有有效通知时,显示在相应应用的启动器图标上的小圆点。用户可以长按应用图标以显示通知。
这些圆点(称为标志)默认会显示,您的应用无需执行任何操作。不过,在某些情况下,您的通知并不适合显示标志,您可以通过对 NotificationChannel
对象调用 setShowBadge(false)
,针对每个渠道停用标志。由于鸡蛋计时器在给定时间只能有一个有效通知,因此应用图标上的标记对用户来说没有太大好处。在以下步骤中,您将停用标记,仅显示计时器的通知。
- 向鸡蛋计时器的频道创建代码添加
setShowBadge(false)
以停用徽章。
// EggTimerFragment.kt
).apply {
// TODO: Step 2.6 disable badges for this channel
setShowBadge(false)
}
- 再次运行应用,启动计时器,然后观察应用图标。您应该不会在应用图标上看到任何标记。
解决方案代码位于所下载代码的 master 分支中。
- 使用 NotificationManager 类来创建、发送、更新和取消通知。
- 使用 NotificationChannel 对象和 createNotificationChannel 方法为通知设置渠道。
- 使用 addAction() 向通知添加快捷操作。
- 使用 setShowBadge() 启用或停用徽章。
- 使用从 Notification.Style 扩展的样式设置通知的样式
- 使用 NotificationChannel.setImportance() 设置重要性级别
Udacity 课程:
Android 开发者文档:
如需本课程中其他 Codelab 的链接,请参阅“使用 Kotlin 进行高级 Android 开发”Codelab 着陆页。