26、Notifications:通知渠道、通知样式、Android 13通知权限
通知,说白了就是App跟用户打招呼的方式。但Android的通知系统这些年改了不少,尤其是Android 8.0引入通知渠道,Android 13又搞了个运行时权限。我刚开始接手一个老项目时,发现通知发不出去,查了半天——原来是没适配通知渠道。嗯,这坑我踩过。
通知渠道(Notification Channel)
Android 8.0(API 26)之后,所有通知都必须绑定一个渠道。渠道就像分类标签,用户可以在系统设置里单独控制每个渠道的开关、声音、震动等。我个人习惯把通知分成三类:
- 重要消息:比如订单支付成功、紧急提醒。用高优先级,带声音震动。
- 普通消息:比如活动推送、内容更新。用默认优先级,声音可选。
- 服务消息:比如后台下载进度、音乐播放。用最低优先级,甚至静音。
创建渠道的代码很简单,但要注意——渠道一旦创建,除了名称和描述,其他属性基本改不了。我遇到过产品经理要求改渠道声音,结果发现只能删掉App重新安装才能生效。所以,渠道设计要提前想清楚。
// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = "order_channel"
val channelName = "订单通知"
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(channelId, channelName, importance).apply {
description = "接收订单相关的推送消息"
enableVibration(true)
setSound(
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION),
Notification.AUDIO_ATTRIBUTES_DEFAULT
)
}
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
小技巧:渠道ID最好用常量定义,别写死字符串。我习惯在companion object里统一管理,方便后续维护。
通知样式:让通知更好看
Android提供了几种内置样式,不用自己画布局。常用的有:
| 样式 | 适用场景 | 说明 |
|---|---|---|
| BigTextStyle | 长文本消息 | 展开后显示完整内容,适合新闻、邮件 |
| InboxStyle | 多条消息汇总 | 像聊天软件那样列出多条记录 |
| BigPictureStyle | 图片推广 | 展开后显示大图,适合营销活动 |
| MessagingStyle | 即时通讯 | 支持会话模式,显示头像和时间戳 |
举个例子,我用MessagingStyle做过一个客服聊天通知,效果比普通通知好很多。用户可以直接在通知栏看到对话上下文,点击还能快速回复。
// 使用 MessagingStyle
val person = Person.Builder()
.setName("客服小张")
.setIcon(Icon.createWithResource(this, R.drawable.ic_support))
.build()
val message1 = Notification.MessagingStyle.Message(
"您好,请问有什么可以帮您?",
System.currentTimeMillis() - 60000,
person
)
val message2 = Notification.MessagingStyle.Message(
"我想查询一下订单状态",
System.currentTimeMillis(),
null // 用户自己发的消息,不设person
)
val notification = Notification.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_notification)
.setStyle(Notification.MessagingStyle(person)
.addMessage(message1)
.addMessage(message2))
.build()
注意:MessagingStyle要求API 28以上。如果App要兼容低版本,记得做版本判断。我曾经因为没加判断,在Android 7的设备上直接崩溃了。
Android 13 通知权限
Android 13(API 33)把通知权限变成了运行时权限。也就是说,App必须像请求相机、定位那样,弹窗让用户同意才能发通知。这个改动影响很大——用户如果拒绝,你的通知就全废了。
权限声明很简单,在AndroidManifest里加上:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
然后在代码里请求权限:
// 检查并请求通知权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
NOTIFICATION_PERMISSION_REQUEST_CODE
)
}
}
这里有个坑:用户拒绝后,系统会弹出一个「不再询问」的选项。如果用户勾选了,你就再也弹不了权限请求了。我建议在用户拒绝后,引导他去系统设置里手动打开。代码可以这样写:
// 用户拒绝后,引导去设置
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
if (requestCode == NOTIFICATION_PERMISSION_REQUEST_CODE) {
if (grantResults.isNotEmpty() &&
grantResults[0] != PackageManager.PERMISSION_GRANTED) {
// 用户拒绝了,弹个Dialog引导去设置
showSettingsDialog()
}
}
}
private fun showSettingsDialog() {
AlertDialog.Builder(this)
.setTitle("需要通知权限")
.setMessage("为了及时收到消息,请在设置中开启通知权限")
.setPositiveButton("去设置") { _, _ ->
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.parse("package:$packageName")
}
startActivity(intent)
}
.setNegativeButton("取消", null)
.show()
}
核心要点:Android 13的通知权限是可选权限。用户拒绝后,App不能崩溃,也不能反复弹窗骚扰用户。优雅处理拒绝,才是专业做法。
知识体系总览
下面这张图梳理了本章的核心逻辑,从渠道创建到样式选择,再到权限处理,一条线串起来:
你看,通知这件事,从渠道到样式再到权限,环环相扣。渠道是基础,样式是体验,权限是门槛。少了哪一环,通知都可能出问题。
我的建议:新项目一开始就把通知渠道设计好,别等上线了再改。权限请求的时机也很重要——别一启动就弹,等用户真正需要通知时再请求,成功率会高很多。
公众号:蓝海资料掘金营,微信deep3321