Android通知的使用
当用户在使用APP的过程中,如果某些重要的功能需要及时提醒用户知道而且在不影响用户的前提下,我们通常会使用通知在后台提醒用户。当然,在实际的过程中,触发通知的点不仅仅只有这一条,所以通知的内容也是必须掌握的功能之一,下面就一起来看看通知。
通知的简单写法
// 开启普通通知
fun normalNotificationClick(view: View) {
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
notificationManager.createNotificationChannel(channel)
}
val mBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher)//小图标
.setContentTitle("我是标题")
.setContentText("我是内容内容")
notificationManager.notify(1, mBuilder.build())
}
这是通知中最简单的写法,需要注意的是,在android 8.0以后,系统就要求必须为所发出的通知创建渠道。为什么要去创建渠道呢?其实也很好理解,就是给通知分组,因为一个APP里面可能要接收的通知很多,有些是很重要的通知,有些是不那么重要的通知,如果按照以前的处理方式,这些通知会被糅杂在一起,我们无法对这些通知做筛选,无法去关闭或者打开我们想操作的那些通知。但是如果使用分组的话就可以完美的去解决这个问题,我们给同类型的通知分为一组,由用户自己来决定是否开启或者关闭,同时给每一个组里面的通知设置不同的提醒方式,这样的设计方式确实比之前好多了。讲了那么多,下面来看看关于通知的渠道的使用。
通知渠道的使用
创建通知渠道
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
notificationManager.createNotificationChannel(channel)
}
我们知道,通知渠道最关键的一个类就是NotificationChannel,那么我们来看看它有哪些比较重要的方法。
方法 | 含义 |
---|---|
getId() | 获取渠道ID |
enableLights(boolean boolean) | 是否开启指示灯(是否在桌面icon右上角展示小红点) |
setLightColor() | 设置指示灯颜色 |
enableVibration() | 是否开启震动 |
setVibrationPattern() | 设置震动频率 |
setImportance() | 设置渠道重要性 |
getImportance() | 获取渠道重要性 |
setSound() | 设置声音 |
getSound() | 获取声音 |
setGroup() | 设置渠道对应的组 |
getGroup() | 得到渠道对应的组 |
setBypassDnd() | 设置绕过免打扰模式 |
canBypassDnd() | 检测是否绕过免打扰模式 |
getName() | 获取通知渠道名称 |
setLockScreenVisibility() | 设置是否应在锁定屏幕上显示此频道的通知 |
getLockscreenVisibility() | 检测能否在锁定屏幕上显示此频道的通知 |
setShowBadge() | 设置是否显示角标 |
canShowBadge() | 检测是否显示角标 |
这些属性都很好理解,唯一需要去重点关注的就是setImportance()这个参数,根据官网文档的解释,对这个参数的理解如下:
用户可见的重要性级别 | 重要性(Android 8.0 及更高版本) | 优先级(Android 7.1 及更低版本) |
---|---|---|
紧急 :发出提示音,并以浮动通知的形式显示 | IMPORTANCE_HIGH | PRIORITY_HIGH 或 PRIORITY_MAX |
高 :发出提示音 | IMPORTANCE_DEFAULT | PRIORITY_DEFAULT |
中 :无提示音 | IMPORTANCE_LOW | PRIORITY_LOW |
低 :无提示音,且不会在状态栏中显示。 | IMPORTANCE_MIN | PRIORITY_MIN |
提醒:如需适配8.0以下的版本,还需要对每条通知设置setPriority()。
读取通知渠道
用户可以修改通知渠道的设置,其中包括振动和提醒提示音等行为。如果您想了解用户对您的通知渠道所应用的设置,请按以下步骤操作:
-
通过调用 getNotificationChannel()或 getNotificationChannels()来获取 NotificationChannel 对象。
-
查询特定的渠道设置,例如 getVibrationPattern()、getSound()和 getImportance()。
接下来,如果您检测到某项渠道设置禁止应用的预期行为,可以建议用户更改该设置,并提供一项用于打开渠道设置的操作
打开通知渠道
创建通知渠道后,您便无法以编程方式更改通知渠道的视觉和听觉行为,只有用户可以通过系统设置更改渠道行为。为了让用户轻松访问这些通知设置,您应在应用的设置界面中添加一个用于打开这些系统设置的项。
您可以通过一个使用 ACTION_CHANNEL_NOTIFICATION_SETTINGS操作的 Intent 打开通知渠道的系统设置。
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
putExtra(Settings.EXTRA_CHANNEL_ID, myNotificationChannel.getId())
}
startActivity(intent)
删除通知渠道
您可以通过调用 deleteNotificationChannel() 删除通知渠道
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val id: String = "my_channel_01"
notificationManager.deleteNotificationChannel(id)
通知创建的使用
床架通知一般分为几个步骤:
(1)创建一个NotificationManager
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
(2)设置通知渠道
(3)使用Builder构造器来创建Notification对象
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher)//小图标
.setContentTitle("我是标题")
.setContentText("我是内容内容")
.build()
首先需要注意的是,在Builder的构造方法里面通常也需要传入渠道ID,来标识该条通知是归属于哪个渠道下面的,那么对于Builder来说,又有哪些可供选择的方法呢?
方法 | 含义 |
---|---|
setAutoCancel(boolean boolean) | 设置点击通知后自动清除通知 |
setContent(RemoteView view) | 设置自定义通知 |
setContentTitle(String string) | 设置通知的标题内容 |
setContentText(String string) | 设置通知的正文内容 |
setContentIntent(PendingIntent intent) | 设置点击通知后的跳转意图 |
setWhen(long when) | 设置通知被创建的时间 |
setSmallIcon(int icon) | 设置通知的小图标 注意:只能使用纯alpha图层的图片进行设置,否则小图标会显示为纯灰 |
setLargeIcon(Bitmap icon) | 设置通知的大图标下拉系统状态栏时就能看见 |
setPriority(int pri) | 设置通知的重要程度 |
setStyle(Style style) | 设置通知的样式 比如设置长文字、大图片等等 |
setVisibility(int defaults) | 设置默认 |
setLight(int argb, int onMs, int offMs) | 设置呼吸闪烁效果 |
setSound(Uri sound) | 设置通知音效 |
setVibrate(long[] pattern) | 设置震动效果,数组包含手机静止时长和震动时长 下标0代表手机静止时长 下标1代表手机整的时长 下标2代表手机静止时长 下标3,4,5.......以此类推 还需要在AndroidManifest.xml中声明权限: <uses-permission android:name="android.permission.VIBRATE"/> |
setColor(int argb) | 设置通知栏颜色 |
setCategory(String category) | 设置通知类别 |
setFullScreenIntent(PendingIntent intent, boolean b) | 设置弹窗显示 |
这些属性都很好理解,唯一需要去重点关注的就是setPriority(int pri)这个参数,因为在搭载 Android 8.0(API 级别 26)及更高版本的设备上,通知的重要程度由通知发布到的渠道的 importance决定。用户可以在系统设置中更改通知渠道的重要程度(图 12)。 在搭载 Android 7.1(API 级别 25)及更低版本的设备上,每条通知的重要程度均由通知的 priority 决定。根据官网文档的解释,对这个参数的理解如下:
用户可见的重要性级别 | 重要性(Android 8.0 及更高版本) | 优先级(Android 7.1 及更低版本) |
---|---|---|
紧急 :发出提示音,并以浮动通知的形式显示 | IMPORTANCE_HIGH | PRIORITY_HIGH 或 PRIORITY_MAX |
高 :发出提示音 | IMPORTANCE_DEFAULT | PRIORITY_DEFAULT |
中 :无提示音 | IMPORTANCE_LOW | PRIORITY_LOW |
低 :无提示音,且不会在状态栏中显示。 | IMPORTANCE_MIN | PRIORITY_MIN |
(4)发送通知
notificationManager.notify(1, notification)
需要注意的是,相同的通知ID,后面的通知会覆盖掉前面的通知。
常见的通知
开启普通通知
fun normalNotificationClick(view: View) {
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher)//小图标
.setContentTitle("我是标题")
.setContentText("我是内容内容")
.build()
notificationManager.notify(1, notification)
}
大文本通知
fun bigTextNotificationClick(view: View) {
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
notificationManager.createNotificationChannel(channel)
}
// 设置点击通知打开事件
val intent = Intent(this, NotificationContentActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
// 创建通知
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("My notification")
.setContentText("Much longer text that cannot fit one line...")
.setStyle(
NotificationCompat.BigTextStyle()
.bigText("Much longer text that cannot fit one line,Much longer text that cannot fit one line,Much longer text that cannot fit one line,Much longer text that cannot fit one line,")
)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setGroup(GROUP_KEY_WORK_EMAIL)
.setAutoCancel(true)
// 显示通知
with(NotificationManagerCompat.from(this)) {
notify(2, builder.build())
}
}
添加操作按钮通知
fun addButtonNotificationClick(view: View) {
// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
// 设置点击通知打开事件
val intent = Intent(this, NotificationContentActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val snoozeIntent = Intent(this, MyBroadcastReceiver::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
val snoozePendingIntent = PendingIntent.getBroadcast(this, 0, snoozeIntent, 0)
// 创建通知
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("My notification")
.setContentText("Much longer text that cannot fit one line,Much longer text that cannot fit one line,Much longer text that cannot fit one line")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setGroup(GROUP_KEY_WORK_EMAIL)
.addAction(R.drawable.message, getString(R.string.snooze), snoozePendingIntent)
// 显示通知
with(NotificationManagerCompat.from(this)) {
notify(3, builder.build())
}
}
开启进度条通知
private var progress_current = 0
private var progressBuilder: NotificationCompat.Builder? = null
fun progressNotificationClick(view: View) {
updateNotification()
}
private fun updateNotification() {
Thread(Runnable {
progress_current++
Thread.sleep(1000)
runOnUiThread {
if (progress_current < 2) {
// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
val notificationManager: NotificationManager =
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).also {
it.createNotificationChannel(channel)
}
}
// 创建通知
progressBuilder = NotificationCompat.Builder(this, CHANNEL_ID).apply {
setContentText("Download in progress")
setContentTitle("Picture Download")
setGroup(GROUP_KEY_WORK_EMAIL)
setSmallIcon(R.mipmap.ic_launcher_round)
}
}
NotificationManagerCompat.from(this).apply {
progressBuilder!!.setProgress(100, progress_current, false)
notify(4, progressBuilder!!.build())
}
if (progress_current <= 50) {
updateNotification()
}
}
}).start()
}
开启多媒体通知
fun mediaNotificationClick(view: View) {
// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
val intent = Intent(this, MyBroadcastReceiver::class.java)
val prevPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0)
val pausePendingIntent = PendingIntent.getBroadcast(this, 1, intent, 0)
val nextPendingIntent = PendingIntent.getBroadcast(this, 2, intent, 0)
val albumArtBitmap = BitmapFactory.decodeResource(resources, R.drawable.picture)
var notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setSmallIcon(R.mipmap.ic_launcher_round)
.addAction(R.drawable.notification_last, "Previous", prevPendingIntent) // #0
.addAction(R.drawable.notification_play, "Pause", pausePendingIntent) // #1
.addAction(R.drawable.notification_next, "Next", nextPendingIntent) // #2
.setStyle(
androidx.media.app.NotificationCompat.MediaStyle()
.setShowActionsInCompactView(1 /* #1: pause button \*/)
.setMediaSession(MediaSessionCompat(this, "MusicService").sessionToken)
)
.setContentTitle("Wonderful music")
.setContentText("My Awesome Band")
.setGroup(GROUP_KEY_WORK_EMAIL)
.setLargeIcon(albumArtBitmap)
.build()
// 显示通知
with(NotificationManagerCompat.from(this)) {
notify(5, notification)
}
}
添加大图片通知
fun bigPictureNotificationClick(view: View) {
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
notificationManager.createNotificationChannel(channel)
}
// 设置点击通知打开事件
val intent = Intent(this, NotificationContentActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
// 创建Bitmap
val myBitmap = BitmapFactory.decodeResource(resources, R.drawable.picture)
// 创建通知
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("这是通知的标题")
.setContentText("这是通知的内容")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setGroup(GROUP_KEY_WORK_EMAIL)
.setStyle(NotificationCompat.BigPictureStyle().bigPicture(myBitmap).bigLargeIcon(null))
// 显示通知
notificationManager.notify(7, builder.build())
}
收件箱样式的通知
fun emailNotificationClick(view: View) {
// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
// 设置点击通知打开事件
val intent = Intent(this, NotificationContentActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
// 创建Bitmap
val myBitmap = BitmapFactory.decodeResource(resources, R.drawable.picture)
// 创建通知
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("这是通知的标题")
.setContentText("这是通知的内容")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setLargeIcon(myBitmap)
.setGroup(GROUP_KEY_WORK_EMAIL)
.setStyle(
NotificationCompat.InboxStyle().addLine("第一条资讯").addLine("第二条资讯").addLine("第三条资讯")
)
.setAutoCancel(true)
// 显示通知
with(NotificationManagerCompat.from(this)) {
notify(8, builder.build())
}
}
自定义通知布局
fun customNotificationClick(view: View) {
// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
// 创建显示的视图
val notificationLayout = RemoteViews(packageName, R.layout.layout_notification_small)
val notificationLayoutExpanded = RemoteViews(packageName, R.layout.layout_notification)
// 创建通知内容
val customNotification = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setCustomContentView(notificationLayout)
.setCustomBigContentView(notificationLayoutExpanded)
.build()
// 发送通知
with(NotificationManagerCompat.from(this)) {
notify(9, customNotification)
}
}
通知的点击
对于一个通知而言,它显示的消息是有限的,一般仅用于提示一些概要信息。但是一般简短的消息,并不能表达需要告诉用户的全部内容,所以需要绑定一个意图,当用户点击通知的时候,调用一个意图展示出一个Activity用来显示详细的内容。而Notification中,并不使用常规的Intent去传递一个意图,而是使用PendingIntent。
Intent和PendingIntent的区别:PendingIntent可以看做是对Intent的包装,通过名称可以看出PendingIntent用于处理即将发生的意图,而Intent用来用来处理马上发生的意图。而对于通知来说,它是一系统级的全局通知,并不确定这个意图被执行的时间。当在应用外部执行PendingIntent时,因为它保存了触发应用的Context,使得外部应用可以如在当前应用中一样,执行PendingIntent里的Intent,就算执行的时候响应通知的应用已经被销毁了,也可以通过存在PendingIntent里的Context照常执行它,并且还可以处理Intent说带来的额外信息。 因此可以将PendingIntent看做是延迟执行的Intent。
获取PendingInten实例可以根据需求从如下方法中获取:
PendingInteng.getBroadcast(contex, requestCode, intent, flags)
PendingInteng.getService(contex, requestCode, intent, flags)
PendingInteng.getActivity(contex, requestCode, intent, flags)
PendingInteng.getActivities(contex, requestCode, intent, flags)
其中flags属性参数用于确定PendingIntent的行为:
FLAG_ONE_SHOT: 表示返回的PendingIntent仅能执行一次,执行完后自动消失
FLAG_NO_CREATE: 表示如果描述的PendingIntent不存在,并不创建相应的PendingIntent,而是返回NULL
FLAG_CANCEL_CURRENT: 表示相应的PendingIntent已经存在,则取消前者,然后创建新的PendingIntent
FLAG_UPDATE_CURRENT: 表示更新的PendingIntent,如果构建的PendingIntent已经存在,则替换它,常用。
PendingIntent的使用也很简答:
(1)创建点击触发的intent
val intent = Intent(this, MyBroadcastReceiver::class.java)
(2)创建PendingIntent
val pendingIntent= PendingIntent.getBroadcast(this, 0, intent, 0)
需要注意的是,创建PendingIntent的时候选择的getXXX一定要和创建的intent相对应。
(3)在builder里面添加
setContentIntent(pendingIntent)
到这里,关于通知的基本内容就结束了,当然在实际的开发过程中还会有一些需要注意的点,比如通知小图标的问题,分组通知等等,这个就要具体问题具体分析了。