7、通知分组与聚合:setGroup()方法、GroupAlertBehavior、摘要通知、Android 7.0+的通知分组机制
通知分组,说白了就是把一堆同类型的通知「打包」在一起。
我刚开始做Android开发那会儿,手机上的通知栏还是一片混乱。某个聊天App来十条消息,通知栏就占十行。用户想划掉都得划半天。后来Android 7.0(API 24)引入了通知分组机制,这个问题才算真正解决。
今天我们就来聊聊这个机制。我会把setGroup()、GroupAlertBehavior、摘要通知这些概念串起来讲。你想想看,如果能把一堆通知折叠成一个组,用户体验是不是好很多?
7.1 为什么需要通知分组?
先问个问题:你的App会不会在短时间内发送多条通知?
比如即时通讯App,群里有人@你,好友发来消息,系统通知又弹出来。如果不做分组,通知栏就会变成这样:
- 张三:晚上一起吃饭?
- 李四:文件发你了
- 系统:群「Android开发」有新消息
- 王五:在吗?
- ……(继续往下刷)
用户看到这种场景,第一反应就是「烦」。我见过不少用户因为通知太乱,直接把App的通知权限关了。嗯,这其实挺可惜的。
通知分组的核心价值就两点:
- 减少视觉噪音:多条通知折叠成一条,用户想看再展开
- 批量操作:用户可以直接清除整个分组,不用一条一条划
核心原则:同一类通知归为一组。比如「聊天消息」一组,「系统提醒」一组,「促销活动」一组。别把风马牛不相及的通知塞进同一个组。
7.2 setGroup():给通知「贴标签」
实现分组的第一步,就是给通知设置一个组ID。方法很简单:
// 创建通知时,指定组ID
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_message)
.setContentTitle("新消息")
.setContentText("你有3条未读消息")
.setGroup("group_chat_messages") // 关键:设置组ID
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(notificationId, builder.build());
这里有个细节要注意:setGroup()的参数是一个字符串,你可以自由定义。但我的习惯是用「模块名_类型」这种命名方式,比如group_chat_private、group_chat_group、group_system_update。这样后期维护起来一目了然。
同一组ID的通知,系统会自动把它们聚合在一起。但光有setGroup()还不够,你还需要一个「摘要通知」来代表整个组。
7.3 摘要通知:分组的「门面」
摘要通知是什么?说白了就是分组的「封面」。用户看到的是摘要通知,点开才能看到组里的具体通知。
创建摘要通知需要做两件事:
- 设置
setGroup()为相同的组ID - 调用
setGroupSummary(true)标记为摘要
// 创建摘要通知
Notification summaryNotification = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_group)
.setContentTitle("聊天消息")
.setContentText("你有5条未读消息")
.setGroup("group_chat_messages")
.setGroupSummary(true) // 标记为摘要通知
.setAutoCancel(true)
.build();
// 摘要通知的ID需要固定,方便后续更新
manager.notify(SUMMARY_NOTIFICATION_ID, summaryNotification);
我在项目中遇到过一个问题:摘要通知和子通知的ID不能冲突。子通知的ID可以动态生成(比如用时间戳),但摘要通知的ID最好固定。否则每次更新摘要时,系统会把它当成新通知,导致分组逻辑混乱。
小技巧:摘要通知的ID建议用一个常量,比如private static final int SUMMARY_NOTIFICATION_ID = 1000;。这样你在更新摘要时,直接传入这个ID就行。
7.4 GroupAlertBehavior:控制通知的「出场方式」
分组之后,通知的提醒行为也需要精细控制。比如:
- 是每条子通知都响铃震动?
- 还是只让摘要通知响?
- 或者干脆静默?
这就用到了setGroupAlertBehavior()方法。它有三个可选值:
| 常量 | 含义 | 适用场景 |
|---|---|---|
GROUP_ALERT_ALL |
组内所有通知都正常提醒 | 重要消息,每条都需要用户注意 |
GROUP_ALERT_SUMMARY |
只提醒摘要通知,子通知静默 | 普通聊天消息,避免频繁打扰 |
GROUP_ALERT_CHILDREN |
只提醒子通知,摘要通知静默 | 摘要只是占位,具体内容才重要 |
// 示例:只让摘要通知提醒
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_message)
.setContentTitle("新消息")
.setContentText("你有3条未读消息")
.setGroup("group_chat_messages")
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY); // 只提醒摘要
我曾经踩过一个坑:某个版本中,我忘了设置setGroupAlertBehavior(),结果所有子通知都疯狂响铃,用户直接给了差评。后来我养成了习惯——只要用了setGroup(),就顺手把提醒行为也写上。
7.5 Android 7.0+ 的分组机制:系统怎么「折叠」通知?
Android 7.0开始,系统对通知分组有了原生支持。它的工作流程是这样的:
- App发送多条通知,这些通知有相同的
setGroup()值 - 系统检测到同一组ID的通知超过1条,就会自动聚合
- 如果App提供了摘要通知,系统会优先显示摘要
- 用户点击摘要通知,可以展开查看所有子通知
- 用户清除摘要通知,整个组都会被清除
这里有个关键点:摘要通知不是必须的。如果你只设置了setGroup()但没有摘要通知,系统会自己生成一个默认的摘要。但默认摘要的样式很丑,而且内容不可控。所以我建议你永远手动创建摘要通知。
注意:在Android 8.0(API 26)之后,通知分组的行为和通知渠道(NotificationChannel)有交互。如果你给某个渠道设置了setShowBadge(false),那么该渠道的通知即使分组了,也不会显示角标。这一点容易忽略,我在项目里吃过亏。
7.6 完整示例:一个聊天App的通知分组
说了这么多,我们来看一个完整的例子。假设你正在做一个聊天App,用户同时收到来自三个好友的消息:
// 1. 创建通知渠道(Android 8.0+ 必须)
NotificationChannel channel = new NotificationChannel(
"chat_channel",
"聊天消息",
NotificationManager.IMPORTANCE_HIGH
);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
// 2. 发送子通知(每条消息一个通知)
String groupId = "group_chat";
int[] childIds = {101, 102, 103};
String[] messages = {"张三:晚上一起吃饭?", "李四:文件发你了", "王五:在吗?"};
for (int i = 0; i < messages.length; i++) {
Notification childNotif = new NotificationCompat.Builder(context, "chat_channel")
.setSmallIcon(R.drawable.ic_message)
.setContentTitle("新消息")
.setContentText(messages[i])
.setGroup(groupId)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY) // 只提醒摘要
.setAutoCancel(true)
.build();
manager.notify(childIds[i], childNotif);
}
// 3. 发送摘要通知
Notification summaryNotif = new NotificationCompat.Builder(context, "chat_channel")
.setSmallIcon(R.drawable.ic_group)
.setContentTitle("聊天消息")
.setContentText("你有3条未读消息")
.setGroup(groupId)
.setGroupSummary(true)
.setAutoCancel(true)
.build();
manager.notify(1000, summaryNotif); // 固定ID
这段代码跑起来之后,通知栏只会显示一条「聊天消息」的摘要。用户下拉通知栏,才能看到三条具体的消息。清爽多了。
7.7 避坑指南:我踩过的那些坑
最后分享几个实战中容易翻车的地方:
- 摘要通知的ID不能和子通知重复:否则系统会混淆,导致分组失败。我建议摘要ID用1000以上的固定值,子通知ID动态生成。
- 不要忘记设置
setGroupAlertBehavior():默认行为是GROUP_ALERT_ALL,如果你不想每条都响,记得改。 - Android 7.0以下不支持分组:如果你的App还要兼容低版本,记得用
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)做判断。 - 摘要通知的
setContentText()要动态更新:比如「你有3条未读消息」变成「你有4条未读消息」。我见过有人写死文本,结果用户看到数量不对,体验很差。
嗯,通知分组其实不难,但细节不少。你只要记住:setGroup()贴标签,摘要通知做封面,提醒行为要明确。这三句话能帮你搞定90%的场景。
一句话总结:通知分组 = setGroup() + 摘要通知 + GroupAlertBehavior。三者缺一不可。
这张图把整个流程串起来了。你从App发送通知开始,经过setGroup()分组,然后判断有没有摘要通知——有就用自定义的,没有就用系统默认的。最终在通知栏里,所有同组通知都会被折叠起来。
好了,通知分组的内容就聊到这里。下一节我们会讲通知的优先级和重要性分级,那个话题也挺有意思的。