二十三、通知的Stack与排序:通知的排序规则、setSortKey()、overrideGroupAlert()、通知的堆叠展示
通知多了怎么办?这是个好问题。
我记得刚做Android开发那会儿,用户一投诉就是“通知栏乱成一锅粥”。尤其是那些IM类的App,消息一多,整个通知栏就跟菜市场似的。后来我深入研究了一下NotificationManager的排序和堆叠机制,才发现——原来系统早就给了我们一套完整的解决方案,只是很多人没用对。
通知的排序规则:谁先谁后?
通知在状态栏和通知抽屉里的排序,其实有一套默认规则。说白了,系统会按以下几个维度来排:
- 优先级(Priority):高优先级的通知排前面。这个从Android 8.0开始被渠道重要性取代,但底层逻辑类似。
- 时间(Time):默认按发布时间倒序,最新的在最上面。
- 本地排序键(Sort Key):这个是我们开发者可以主动控制的。
嗯,这里要注意:系统默认的排序,对大多数场景已经够用了。但如果你想让某条通知“插队”或者“置顶”,那就得靠 setSortKey() 了。
setSortKey():手动控制排序的利器
我在项目中遇到过这样一个需求:音乐播放器的通知,必须始终显示在通知栏最顶部,不管用户发了多少条消息。用优先级?不行,因为其他App也可能用高优先级。那怎么办?
答案就是 setSortKey()。这个方法允许你传入一个字符串,系统会按字符串的字典序来排序。字符串越“小”,排得越靠前。
// 让音乐通知始终排在最前面
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("正在播放:晴天")
.setContentText("周杰伦")
.setSmallIcon(R.drawable.ic_music)
.setSortKey("A"); // 用"A"开头,字典序最小
// 普通消息通知
NotificationCompat.Builder msgBuilder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("新消息")
.setContentText("你好")
.setSmallIcon(R.drawable.ic_message)
.setSortKey("Z"); // 用"Z"开头,排后面
"A" 或 "0" 作为前缀。但别用空字符串,空字符串的排序行为在不同ROM上不一致,我踩过这个坑。
你可能会问:那如果多个通知都用 "A" 呢?它们之间怎么排?答案是:按时间倒序。所以 setSortKey() 只是决定了“组”的顺序,组内还是按时间。
overrideGroupAlert():控制组的提醒行为
说到分组通知,就不得不提 overrideGroupAlert()。这个方法是在Android 8.0引入的,用来控制当通知属于某个组时,是否要发出声音、震动等提醒。
举个例子:你有一个聊天App,每个会话是一个组。用户可能同时收到几十条消息,如果每一条都响一声,那手机就炸了。这时候,你可以用 overrideGroupAlert() 来设定:只有组里的第一条通知才提醒,后面的静默追加。
// 创建组摘要通知
NotificationCompat.Builder summaryBuilder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setGroup("chat_group_123")
.setGroupSummary(true)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); // 只在摘要通知时提醒
// 创建子通知
NotificationCompat.Builder childBuilder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setGroup("chat_group_123")
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN); // 子通知不单独提醒
| 常量 | 含义 | 适用场景 |
|---|---|---|
GROUP_ALERT_ALL |
所有通知都提醒(默认) | 重要消息,每条都要用户知道 |
GROUP_ALERT_SUMMARY |
只在摘要通知时提醒 | 聊天群组,避免轰炸 |
GROUP_ALERT_CHILDREN |
子通知不单独提醒 | 配合摘要使用,静默追加 |
overrideGroupAlert() 是不生效的。如果你要兼容老版本,得自己判断SDK版本,或者用 setDefaults(0) 来手动关闭声音。别问我怎么知道的——线上用户反馈说“手机一直响”,我才发现是兼容性问题。
通知的堆叠展示:当通知多到放不下
你有没有想过,为什么有时候通知栏只显示“3条新消息”,而不是把每条消息都列出来?这就是通知的堆叠展示(Stacking)。
系统默认会对同一个App的通知进行堆叠。但堆叠的规则是什么?说白了,就是按 setGroup() 来分组的。同一个组里的通知,会被折叠成一条摘要,点开才能看到详情。
我个人的习惯是:对于IM类App,每个会话用一个独立的group key。这样用户看到的就是“会话A: 3条消息”、“会话B: 2条消息”,而不是一长串混在一起。
// 为每个会话生成唯一的group key
String groupKey = "chat_" + conversationId;
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setGroup(groupKey)
.setGroupSummary(false) // 子通知
.setContentTitle("张三")
.setContentText("晚上一起吃饭吗?")
.setSortKey("A_" + System.currentTimeMillis()); // 按时间排序
堆叠展示还有一个好处:减少用户的视觉负担。你想想看,如果通知栏里全是“你有一条新消息”,用户很快就会产生通知疲劳,甚至直接关掉你的通知权限。这可不是我们想看到的。
知识体系总览
下面这张图,是我自己总结的通知排序与堆叠的核心逻辑。你看一眼就能明白整个流程:
总结一下
通知的排序和堆叠,说白了就是帮用户“整理桌面”。setSortKey() 让你能控制谁排前面,overrideGroupAlert() 让你能控制谁该出声,而堆叠展示则让通知栏不至于变成一锅粥。
我个人觉得,这三个工具用好之后,用户体验能提升一大截。尤其是那些通知量大的App,千万别偷懒——给每个通知分好组、排好序,用户会感谢你的。
setSortKey()用字符串字典序控制排序,前缀用 "A" 或 "0" 可置顶overrideGroupAlert()控制组内通知的提醒行为,避免轰炸- 堆叠展示依赖
setGroup(),同一个组的通知会被折叠 - 注意兼容性:
overrideGroupAlert()只在 Android 8.0+ 生效