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_privategroup_chat_groupgroup_system_update。这样后期维护起来一目了然。

同一组ID的通知,系统会自动把它们聚合在一起。但光有setGroup()还不够,你还需要一个「摘要通知」来代表整个组。

7.3 摘要通知:分组的「门面」

摘要通知是什么?说白了就是分组的「封面」。用户看到的是摘要通知,点开才能看到组里的具体通知。

创建摘要通知需要做两件事:

  1. 设置setGroup()为相同的组ID
  2. 调用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开始,系统对通知分组有了原生支持。它的工作流程是这样的:

  1. App发送多条通知,这些通知有相同的setGroup()
  2. 系统检测到同一组ID的通知超过1条,就会自动聚合
  3. 如果App提供了摘要通知,系统会优先显示摘要
  4. 用户点击摘要通知,可以展开查看所有子通知
  5. 用户清除摘要通知,整个组都会被清除

这里有个关键点:摘要通知不是必须的。如果你只设置了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("group_chat") 设置组ID 有摘要通知? 显示摘要通知 系统生成默认摘要 通知栏显示折叠分组

这张图把整个流程串起来了。你从App发送通知开始,经过setGroup()分组,然后判断有没有摘要通知——有就用自定义的,没有就用系统默认的。最终在通知栏里,所有同组通知都会被折叠起来。

好了,通知分组的内容就聊到这里。下一节我们会讲通知的优先级和重要性分级,那个话题也挺有意思的。


公众号:蓝海资料掘金营,微信deep3321