在鸿蒙(HarmonyOS)应用开发中,通知是连接应用与用户的重要桥梁 —— 电商 APP 的订单动态、社交 APP 的消息提醒、工具类 APP 的任务通知等,都依赖通知触达用户。但当同类消息高频推送时(如 10 条订单状态更新、20 条群聊消息),通知栏会被 “刷屏”,不仅干扰用户视线,还可能导致关键信息被淹没。​

幸运的是,鸿蒙通知模块原生支持消息聚合(分组)功能,无需第三方库,只需通过 “通知通道 + 通知分组” 组合配置,即可将同类消息合并为一条通知,折叠时显示摘要,展开时查看详情。本文将从机制解析到代码落地,完整讲解鸿蒙通知聚合的实现方案。​

一、为什么需要通知聚合?

在未做聚合处理的场景中,高频同类通知会带来三大问题:​

  1. 视觉干扰:通知栏被大量重复类型的消息占据,用户需滑动多次才能找到其他重要通知;​
  1. 信息冗余:同类消息的格式、来源一致,重复展示会浪费屏幕空间(尤其在智能手表等小屏设备上);​
  1. 操作低效:用户需逐条点击查看同类消息,无法批量处理(如 “一键标记已读”)。​

而通知聚合能解决这些问题:​

  • 折叠展示:同类消息合并为一条,显示 “XX 条通知” 摘要,减少视觉占用;​
  • 分层查看:点击聚合通知可展开所有单条消息,既保留细节又不占用空间;​
  • 统一管理:用户可对聚合组批量操作(如删除、静音),提升操作效率。​

二、鸿蒙通知聚合的核心机制:两大能力支撑​

鸿蒙通过 “通知通道(NotificationSlot) ” 和 “通知分组(NotificationGroup) ” 两大原生能力,实现通知聚合。两者分工明确、协同工作:

机制逻辑梳理​

  1. 应用先创建 “订单通知” 通道,并开启通道的分组能力;​
  1. 发送第一条订单消息时,同时发送一条 “摘要通知”(标记该分组的总条数);​
  1. 后续发送同订单的消息时,指定相同的groupKey,系统自动将其归入该分组;​
  1. 用户在通知栏看到的是 “1 条摘要通知”(显示 “3 条订单通知”),点击可展开 3 条单条消息。​

三、实战落地:从通道初始化到聚合通知发送​

以 “电商 APP 订单通知” 为例,实现 “同一订单的多条状态更新聚合为一条通知” 的功能,完整步骤如下:​

1. 第一步:初始化通知通道(聚合的 “分类基础”)​

通知通道是鸿蒙通知管理的核心,所有通知必须绑定通道才能推送。要实现聚合,需在通道初始化时开启 “分组支持”。​

关键 API 说明​

  • NotificationSlot:创建通道实例,参数为 “通道 ID(唯一)、通道名称(用户可见)、通道类型”;​
  • setGroupEnabled(true):允许该通道下的通知进行分组;​
  • setGroupSummaryEnabled(true):开启分组摘要显示(折叠时显示 “XX 条通知”);​
  • createNotificationSlot(slot):将通道注册到系统,需在应用启动时执行(仅需执行一次)。
import notification from '@ohos.notification';
import { BusinessError } from '@ohos.base';

// 通道常量定义
const ORDER_NOTIFICATION_SLOT_ID = "order_notification_slot";
const ORDER_NOTIFICATION_SLOT_NAME = "订单通知";

// 初始化通知通道
async function initNotificationSlot() {
    try {
        // 创建通道配置
        const slot: notification.NotificationSlot = {
            slotId: ORDER_NOTIFICATION_SLOT_ID,
            slotName: ORDER_NOTIFICATION_SLOT_NAME,
            slotType: notification.SlotType.SERVICE_INFORMATION,
            groupEnabled: true, // 允许分组
            groupSummaryEnabled: true, // 显示摘要
            lockscreenVisibility: notification.Visibility.PUBLIC,
            ledLightColor: '#FF6600',
            vibrationEnabled: true
        };
        
        // 注册通道
        await notification.notificationHelper.createNotificationSlot(slot);
    } catch (error) {
        const err = error as BusinessError;
        console.error(`初始化订单通知通道失败:${err.code}, ${err.message}`);
    }
}

2. 第二步:发送聚合通知(分组的 “核心逻辑”)​

同一订单的多条状态更新(如 “订单已支付”“订单已发货”“订单已签收”)需归入同一分组,需满足两个条件:​

  • 所有通知绑定同一通道(ORDER_NOTIFICATION_SLOT_ID);​
  • 所有通知设置相同的groupKey(如 “order_123456”,对应订单 ID);​
  • 需发送一条 “摘要通知”,用于折叠状态展示总条数。​

关键 API 说明​

  • NotificationRequest:通知请求实例,参数为 “通知 ID(唯一,建议用哈希值生成)”;​
  • setSlotId(slotId):绑定通知通道;​
  • setGroup(groupKey):指定分组唯一标识(同一分组需相同);​
  • setGroupSummary(isSummary):标记是否为摘要通知(true表示摘要,false表示单条消息);​
  • publishNotification(request):发送通知到系统。​

代码实现(ArkTS 版,含摘要 + 单条通知)

import notification from '@ohos.notification';
import { BusinessError } from '@ohos.base';

// 订单通知聚合发送工具类
class OrderNotificationSender {
    // 分组标识(格式:order_+订单ID,确保同一订单的通知归为一组)
    private getGroupKey(orderId: string): string {
        return `order_group_${orderId}`;
    }
    
    // 生成唯一通知ID(避免不同通知覆盖)
    private getNotificationId(content: string): number {
        return content.hashCode() + Date.now();
    }
    
    /**
     * 发送订单聚合通知
     * @param orderId 订单ID(用于分组)
     * @param orderStatus 订单状态(如“已支付”“已发货”)
     * @param notifyContent 通知详情内容
     * @param totalCount 该订单的通知总条数(用于摘要)
     */
    async sendAggregatedOrderNotification(
        orderId: string,
        orderStatus: string,
        notifyContent: string,
        totalCount: number
    ) {
        try {
            // 1. 先发送“分组摘要通知”(折叠时显示)
            await this.sendGroupSummaryNotification(orderId, totalCount);
            
            // 2. 再发送“单条订单通知”(展开时显示)
            await this.sendSingleOrderNotification(orderId, orderStatus, notifyContent);
        } catch (error) {
            const err = error as BusinessError;
            console.error(`发送订单通知失败:${err.code}, ${err.message}`);
        }
    }
    
    // 发送分组摘要通知(仅需一条,可随总条数更新)
    private async sendGroupSummaryNotification(orderId: string, totalCount: number) {
        const groupKey = this.getGroupKey(orderId);
        const summaryId = this.getNotificationId(`summary_${orderId}`);
        
        // 构建摘要通知内容
        const summaryContent: notification.NotificationContent = {
            title: `您有${totalCount}条订单动态`,
            text: `订单${orderId}的最新状态更新,点击查看详情`,
            additionalText: `${totalCount}条` // 通知右侧显示的条数提示
        };
        
        // 构建通知请求
        const summaryRequest: notification.NotificationRequest = {
            id: summaryId,
            slotId: ORDER_NOTIFICATION_SLOT_ID,
            content: summaryContent,
            group: groupKey, // 绑定分组
            groupSummary: true // 标记为摘要通知
        };
        
        // 发送摘要通知
        await notification.notificationHelper.publishNotification(summaryRequest);
    }
    
    // 发送单条订单通知(每条状态更新对应一条)
    private async sendSingleOrderNotification(
        orderId: string,
        orderStatus: string,
        notifyContent: string
    ) {
        const groupKey = this.getGroupKey(orderId);
        const singleId = this.getNotificationId(`single_${orderId}_${Date.now()}`);
        
        // 构建单条通知内容(可添加跳转参数,点击时打开订单详情页)
        const singleContent: notification.NotificationContent = {
            title: `订单${orderId}:${orderStatus}`,
            text: notifyContent,
            // 点击通知跳转的意图(可选,需配置Ability路由)
            intent: {
                url: `ability://com.example.shop/OrderDetailAbility?orderId=${orderId}`,
                type: 'ability'
            }
        };
        
        // 构建通知请求
        const singleRequest: notification.NotificationRequest = {
            id: singleId,
            slotId: ORDER_NOTIFICATION_SLOT_ID,
            content: singleContent,
            group: groupKey, // 绑定到同一分组
            groupSummary: false // 标记为单条消息
        };
        
        // 发送单条通知
        await notification.notificationHelper.publishNotification(singleRequest);
    }
}

// 调用示例:发送订单123的第2条通知(状态为“已发货”)
const sender = new OrderNotificationSender();
sender.sendAggregatedOrderNotification(
    "123456",
    "已发货",
    "您的订单已由顺丰快递发出,运单号:SF123456789",
    2 // 该订单当前共有2条通知
);

3. 第三步:处理通知交互(展开 / 点击)​

聚合通知的交互体验需重点关注两点:分组展开 / 折叠和点击跳转。​

(1)分组展开 / 折叠​

鸿蒙系统默认支持聚合通知的展开 / 折叠:​

  • 折叠状态:显示 “摘要通知”,右侧标注 “XX 条”;​
  • 展开状态:点击摘要通知,自动展开该分组下的所有单条通知;​
  • 批量操作:长按分组通知,可选择 “全部删除”“静音该分组” 等操作(系统原生支持)。​

(2)点击跳转配置​

通过NotificationContent的intent属性,配置点击通知后的跳转逻辑:​

  • 跳转到应用内页面(如订单详情页):使用ability类型的url,携带订单 ID 等参数;​
  • 跳转到网页:使用url类型的url,直接指定网页地址。​

示例中已配置跳转到OrderDetailAbility,接收参数的代码如下(在OrderDetailAbility的onStart中):

import { AbilityConstant, UIAbility, Want } from '@ohos.app.ability';
import { window } from '@ohos.ui.window';

export default class OrderDetailAbility extends UIAbility {
    onStart(want: Want) {
        // 接收通知跳转携带的订单ID
        const orderId = want.parameters?.orderId as string;
        console.log(`跳转至订单详情页,订单ID:${orderId}`);
        
        // 后续加载订单详情数据...
        super.onStart(want);
    }
}

四、进阶优化:让聚合通知更智能​

1. 动态更新摘要通知的总条数​

当同一分组的通知数量增加时,需更新摘要通知的 “总条数”,避免显示过时信息。实现方式:​

  • 维护每个订单的通知计数器(如用DistributedKVStore存储);​
  • 每次发送新通知前,更新计数器,再调用sendGroupSummaryNotification覆盖旧摘要。
// 维护订单通知计数器(示例:用内存变量,实际建议用本地存储)
private orderNotifyCountMap: Map<string, number> = new Map();

// 更新计数器并发送通知
async sendOrderNotifyWithCount(orderId: string, orderStatus: string, notifyContent: string) {
    // 1. 更新计数器
    const currentCount = (this.orderNotifyCountMap.get(orderId) || 0) + 1;
    this.orderNotifyCountMap.set(orderId, currentCount);
    
    // 2. 发送聚合通知(携带最新计数器)
    await this.sendAggregatedOrderNotification(
        orderId,
        orderStatus,
        notifyContent,
        currentCount
    );
}

2. 按 “时间 / 类型” 细分分组​

若订单通知类型复杂(如 “状态更新”“优惠提醒”“物流通知”),可进一步细分分组:​

  • 分组标识格式改为order_group_${orderId}_${type}(如order_group_123456_logistics);​
  • 不同类型的通知归入不同分组,避免混在一起导致混乱。​

3. 支持用户自定义聚合开关​

在应用内添加 “通知聚合设置”,允许用户控制是否开启聚合:​

  • 若用户关闭聚合,发送通知时不设置group和groupSummary;​
  • 若用户开启聚合,按正常分组逻辑发送。
// 读取用户聚合设置(示例:从本地存储获取)
private async isUserEnableNotificationAggregation(): Promise<boolean> {
    // 实际项目中从KV存储读取,此处简化为默认开启
    return true;
}

// 发送通知时根据用户设置决定是否聚合
async sendNotificationByUserSetting(
    orderId: string,
    orderStatus: string,
    notifyContent: string
) {
    const isAggregated = await this.isUserEnableNotificationAggregation();
    if (isAggregated) {
        // 开启聚合:按分组逻辑发送
        await this.sendOrderNotifyWithCount(orderId, orderStatus, notifyContent);
    } else {
        // 关闭聚合:直接发送单条通知(不设置group)
        await this.sendSingleOrderNotification(orderId, orderStatus, notifyContent);
    }
}

五、常见问题与避坑指南​

1. 分组不生效?检查 3 个关键点​

  • 通知通道是否开启groupEnabled: true?未开启则无法分组;​
  • 所有通知的groupKey是否完全一致?大小写、字符差异都会导致分组失败;​
  • 是否发送了 “摘要通知”?没有摘要通知,系统无法识别分组,会显示多条单条通知。​

2. 摘要通知被覆盖?注意通知 ID 唯一性​

  • 摘要通知的id需固定(如用summary_${orderId}生成),每次更新时用相同id覆盖旧摘要;​
  • 单条通知的id需唯一(如加Date.now()),避免被覆盖。​

3. 小屏设备(如手表)适配​

  • 小屏设备建议简化摘要内容,仅保留核心信息(如 “3 条订单通知”);​
  • 单条通知的文本长度控制在 15 字以内,避免换行导致体验差。​

 

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐