
通知栏是用户感知应用消息的第一入口角标是桌面图标的未读提示——两者配合得好用户体验就顺畅配合得差用户直接关通知权限。这篇把通知发布、WantAgent 点击跳转、角标管理、通知渠道和分组通知的完整方案讲清楚。通知权限申请在发通知之前第一件事不是写代码而是确认通知权限。HarmonyOS 的通知权限是ohos.permission.NOTIFICATION_CONTROLLER但对普通应用来说更关键的是调用requestEnableNotification引导用户手动开启。import{notificationManager}fromkit.NotificationKit;import{BusinessError}fromkit.BasicServicesKit;asyncrequestNotificationPermission():Promiseboolean{// 先检查通知是否已开启constenabled:booleanawaitnotificationManager.isNotificationEnabled();if(enabled){returntrue;}// 未开启则弹系统引导弹窗让用户手动授权try{awaitnotificationManager.requestEnableNotification();returntrue;}catch(e){consterreasBusinessError;console.error(请求通知权限失败:${err.code}-${err.message});returnfalse;}}通知权限不像相机那样在module.json5里声明user_grant它是系统级管控——用户可以在设置里随时关闭。所以每次发通知前最好检查一次状态。注意requestEnableNotification只能弹一次系统授权弹窗用户拒绝后再次调用不会弹出只能引导用户去设置页面手动开启。发布基础文本通知通知的核心是NotificationRequest它定义了通知的 ID、内容类型、标题文本等。同一个 ID 的通知再次发布会覆盖上一条而不是新增一条。interfaceNotifyParams{id:number;title:string;text:string;}asyncpublishBasicNotification(params:NotifyParams):Promisevoid{constrequest:notificationManager.NotificationRequest{id:params.id,content:{notificationContentType:notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,normal:{title:params.title,text:params.text,},},};try{awaitnotificationManager.publish(request);console.info(通知发布成功);}catch(e){consterreasBusinessError;console.error(通知发布失败:${err.code});}}ContentType有几种NOTIFICATION_CONTENT_BASIC_TEXT是基础文本NOTIFICATION_CONTENT_LONG_TEXT支持长文本NOTIFICATION_CONTENT_MULTI_LINE支持多行NOTIFICATION_CONTENT_PICTURE支持图片附件。按需选择即可。关键区别通知 ID 相同 标签相同会覆盖旧通知ID 相同但标签不同会新增一条。如果想要多条消息不互相覆盖每条通知用不同的 ID。WantAgent 点击跳转光发通知没用用户点通知得能跳到对应页面。这就需要WantAgent——它本质上是一个延迟执行的 Intent系统在用户点击通知时帮你触发。import{wantAgent}fromkit.AbilityKit;asyncpublishNotificationWithAction(title:string,text:string,pageUrl:string):Promisevoid{// 构建 WantAgent点击后跳转到指定 Ability 并携带参数constwantAgentInfo:wantAgent.WantAgentInfo{wants:[{bundleName:com.example.myapp,abilityName:EntryAbility,parameters:{pageUrl:pageUrl,},},],operationType:wantAgent.OperationType.START_ABILITIES,requestCode:100,};constwantAgentObjawaitwantAgent.getWantAgent(wantAgentInfo);constrequest:notificationManager.NotificationRequest{id:200,content:{notificationContentType:notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,normal:{title:title,text:text,},},wantAgent:wantAgentObj,};awaitnotificationManager.publish(request);}在EntryAbility的onNewWant回调里读取parameters.pageUrl就能拿到跳转参数再通过router导航到对应页面。这是通知跳转的标准链路。注意requestCode在同一应用内要唯一否则不同 WantAgent 可能互相冲突。建议用常量管理。角标数字管理角标就是桌面图标右上角的红色数字气泡。HarmonyOS 提供两种方式设置角标发布通知时带badgeNumber字段累加模式或调用setBadgeNumber直接设置绝对值模式。// 方式一发布通知时携带 badgeNumber系统自动累加asyncpublishWithBadge(title:string,text:string):Promisevoid{constrequest:notificationManager.NotificationRequest{id:300,badgeNumber:1,content:{notificationContentType:notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,normal:{title:title,text:text},},};awaitnotificationManager.publish(request);}// 方式二直接设置角标绝对值asyncsetBadge(count:number):Promisevoid{try{awaitnotificationManager.setBadgeNumber(count);}catch(e){consterreasBusinessError;console.error(设置角标失败:${err.code});}}// 清除角标asyncclearBadge():Promisevoid{awaitnotificationManager.setBadgeNumber(0);}两种方式的差异很重要badgeNumber是增量每次发布通知时告诉系统增加 N 个未读setBadgeNumber是绝对值直接告诉系统当前未读数是 N。用户点开消息后你需要自己算剩余未读数再setBadgeNumber更新。关键区别setBadgeNumber是异步接口连续调用时必须等上一次完成再调下一次否则执行顺序不可控。用.then()链式调用保证顺序。长文本与多行通知基础文本通知只显示一行信息量有限。如果你需要展示更多内容可以用NOTIFICATION_CONTENT_LONG_TEXT或NOTIFICATION_CONTENT_MULTI_LINE。asyncpublishLongTextNotification(title:string,longText:string):Promisevoid{constrequest:notificationManager.NotificationRequest{id:150,content:{notificationContentType:notificationManager.ContentType.NOTIFICATION_CONTENT_LONG_TEXT,longText:{title:title,text:longText.substring(0,30),longText:longText,briefText:longText.substring(0,20),},},};awaitnotificationManager.publish(request);}asyncpublishMultiLineNotification(title:string,lines:Arraystring):Promisevoid{constrequest:notificationManager.NotificationRequest{id:160,content:{notificationContentType:notificationManager.ContentType.NOTIFICATION_CONTENT_MULTI_LINE,multiLine:{title:title,text:展开查看详情,briefText:${lines.length}条新消息,lines:lines,},},};awaitnotificationManager.publish(request);}长文本通知在折叠态显示text摘要展开后显示完整longText。多行通知折叠态显示briefText展开后逐行显示lines数组的内容。聊天类 App 用多行通知最合适——每条消息一行展开就是完整对话。注意longText和lines的内容在通知栏折叠态不会完整展示只有用户手动展开才能看到全文。所以text和briefText一定要写好摘要吸引用户展开。通知渠道与 SlotAndroid 有 NotificationChannelHarmonyOS 有 NotificationSlot——概念几乎一样。Slot 定义了通知的优先级、声音、振动等行为属性发布通知时系统根据 Slot 类型决定如何展示。asynccreateNotificationSlot():Promisevoid{// 创建社交消息类型渠道默认重要级别constslot:notificationManager.NotificationSlot{type:notificationManager.SlotType.SOCIAL_COMMUNICATION,level:notificationManager.SlotLevel.LEVEL_DEFAULT,desc:社交消息通知,badgeFlag:true,showBadge:true,enableLight:true,enableVibration:true,};try{awaitnotificationManager.addSlot(slot);}catch(e){consterreasBusinessError;console.error(创建 Slot 失败:${err.code});}}SlotType 主要有SOCIAL_COMMUNICATION社交、SERVICE_INFORMATION服务信息、CONTENT_INFORMATION内容信息、OTHER_TYPES其他。不同类型对应不同的系统默认行为。建议在应用启动时就创建好所有 Slot发通知时指定notificationSlotType即可。注意如果发布通知时指定的 SlotType 还没有对应 Slot系统会自动创建一个默认配置的 Slot。但建议显式创建这样你可以控制声音、振动等细节。取消通知通知看完了得能消掉。取消指定 ID 的通知用cancel一键全清用cancelAll按分组取消用cancelGroup。asynccancelSpecificNotification(id:number):Promisevoid{try{awaitnotificationManager.cancel(id);}catch(e){consterreasBusinessError;console.error(取消通知失败:${err.code});}}asynccancelAllNotifications():Promisevoid{try{awaitnotificationManager.cancelAll();}catch(e){consterreasBusinessError;console.error(取消全部通知失败:${err.code});}}// 取消通知后同步更新角标asynconNotificationRead(id:number,remainingCount:number):Promisevoid{awaitnotificationManager.cancel(id);awaitnotificationManager.setBadgeNumber(remainingCount);}关键区别cancel只取消本应用发布的通知不会影响其他应用。cancelAll同理只清本应用的。系统通知中心里所有应用的通知是隔离的。取消通知还有一个常用场景用户点击通知进入应用后应该自动清除对应通知并更新角标。你可以在onPageShow或onNewWant里执行这个逻辑——拿到通知 ID 后cancel再算剩余未读数setBadgeNumber。这是用户体验的细节但很多 App 都忽略了导致通知中心堆满已读消息。分组通知当应用短时间内发多条通知比如聊天消息用分组通知可以把它们折叠显示避免占满通知栏。分组的核心是在NotificationRequest里设置groupName。asyncpublishGroupedNotification(groupId:string,messages:Arraystring):Promisevoid{for(leti0;imessages.length;i){constrequest:notificationManager.NotificationRequest{id:400i,groupName:groupId,content:{notificationContentType:notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,normal:{title:新消息,text:messages[i],},},};awaitnotificationManager.publish(request);}}同一groupName的通知会自动折叠成一组通知栏只显示摘要用户展开才能看各条详情。社交类应用强烈建议用分组不然十条消息就把通知栏塞满了。注意分组名称建议用业务含义比如chat_group_123这样取消整组通知时可以直接cancelGroup(chat_group_123)。分组通知的角标计算也需要注意每组通知的badgeNumber是按组内条数累加的但实际显示在桌面的是所有分组角标数的总和。如果你只想显示未读会话数而不是未读消息数就不能用badgeNumber自动累加需要自己维护一个计数器每有一个未读会话setBadgeNumber加 1会话全部已读后减 1。完整示例页面把前面的内容组合成一个带发送按钮和角标展示的演示页面功能包括发送普通通知、发送带跳转通知、设置角标、清除所有通知。EntryComponentstruct NotificationDemoPage{StatebadgeCount:number0;StatenotifyId:number1;aboutToAppear():void{this.requestNotificationPermission();}asyncrequestNotificationPermission():Promisevoid{constenabledawaitnotificationManager.isNotificationEnabled();if(!enabled){awaitnotificationManager.requestEnableNotification();}}asyncsendBasicNotify():Promisevoid{constrequest:notificationManager.NotificationRequest{id:this.notifyId,badgeNumber:1,content:{notificationContentType:notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,normal:{title:系统提示,text:您有一条新消息},},};awaitnotificationManager.publish(request);this.badgeCount;awaitnotificationManager.setBadgeNumber(this.badgeCount);}asyncsendActionNotify():Promisevoid{constwantAgentInfo:wantAgent.WantAgentInfo{wants:[{bundleName:com.example.myapp,abilityName:EntryAbility,}],operationType:wantAgent.OperationType.START_ABILITIES,requestCode:200,};constagentawaitwantAgent.getWantAgent(wantAgentInfo);constrequest:notificationManager.NotificationRequest{id:this.notifyId,badgeNumber:1,content:{notificationContentType:notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,normal:{title:点击跳转,text:点击这条通知打开应用},},wantAgent:agent,};awaitnotificationManager.publish(request);this.badgeCount;awaitnotificationManager.setBadgeNumber(this.badgeCount);}asyncclearAll():Promisevoid{awaitnotificationManager.cancelAll();this.badgeCount0;awaitnotificationManager.setBadgeNumber(0);}build(){Column({space:16}){Text(通知与角标演示).fontSize(24).fontWeight(FontWeight.Bold)Text(当前角标:${this.badgeCount}).fontSize(18)Button(发送普通通知).width(80%).onClick((){this.sendBasicNotify();})Button(发送跳转通知).width(80%).onClick((){this.sendActionNotify();})Button(清除所有通知).width(80%).onClick((){this.clearAll();})}.width(100%).height(100%).padding(20)}}这个页面覆盖了通知发布、WantAgent 跳转、角标增减和全量取消四个核心操作。实际项目中你可以在onNewWant里处理通知点击回调然后router.pushUrl到具体页面。踩坑清单问题原因解决publish 报 1600004通知权限未开启先调 isNotificationEnabled requestEnableNotification角标数字不更新setBadgeNumber 异步连续调用顺序乱用 .then() 链式保证顺序点击通知无反应未设置 WantAgentNotificationRequest 里加 wantAgent 字段同 ID 通知被覆盖相同 ID 会替换旧通知不同消息用不同 IDrequestEnableNotification 只弹一次系统限制重复弹窗引导用户去设置页手动开启分组通知不折叠未设置 groupName相同业务用相同 groupNameSlot 声音不生效Slot 未提前创建应用启动时 addSlot 初始化cancel 后角标还在cancel 不会自动减角标cancel 后手动 setBadgeNumber 更新通知过多占满通知栏未使用分组多条同类通知设 groupNamebadgeNumber 大于 99 显示异常系统角标上限 99超过 99 显示 99无需特殊处理