HarmonyOS APP中@ohos.notification、NotificationRequest、通知发布与取消
HarmonyOS APP中@ohos.notification、NotificationRequest、通知发布与取消
📌 核心要点:掌握 HarmonyOS 通知体系的核心 API,从 NotificationRequest 构建到通知的发布与取消,打通应用与用户之间的"最后一公里"通信。
一、背景与动机
你有没有这样的经历?手机静音放在桌上,错过了一条重要的快递通知;或者某个 App 疯狂弹通知,让你恨不得直接卸载它。通知,这个看似简单的功能,其实是应用和用户之间最直接的沟通桥梁。
在 HarmonyOS 的世界里,通知不仅仅是"弹个框"那么简单。它是一套完整的消息分发体系——从构建通知内容、选择通知渠道、到最终展示在用户的通知中心,每一个环节都有精细的 API 支持。而这一切的起点,就是 @ohos.notificationManager 模块。
打个比方:通知就像快递系统。NotificationRequest 是你要寄的包裹,通知渠道是快递公司的不同配送线路,而 notificationManager 就是那个快递柜——负责把包裹投递到用户手中。理解了这个类比,后面的 API 学习就会顺畅很多。
二、核心原理
2.1 通知体系架构
HarmonyOS 的通知体系由三层构成:
| 层级 | 职责 | 关键 API |
|---|---|---|
| 应用层 | 构建通知内容、发布/取消通知 | notificationManager.publish() / cancel() |
| 框架层 | 通知路由、渠道管理、优先级排序 | NotificationSlot、NotificationRequest |
| 系统层 | 通知展示、用户交互、权限管控 | 通知中心、状态栏、锁屏 |
flowchart TD
A[应用构建 NotificationRequest] --> B[设置通知内容 NotificationContent]
B --> C[绑定通知渠道 NotificationSlot]
C --> D[调用 notificationManager.publish]
D --> E{系统权限检查}
E -->|通过| F[通知进入系统队列]
E -->|拒绝| G[抛出权限异常]
F --> H[根据渠道优先级排序]
H --> I[展示在通知中心/状态栏/锁屏]
classDef primary fill:#4CAF50,stroke:#388E3C,color:#fff
classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
classDef error fill:#F44336,stroke:#D32F2F,color:#fff
classDef info fill:#2196F3,stroke:#1976D2,color:#fff
classDef purple fill:#9C27B0,stroke:#7B1FA2,color:#fff
class A,B,C,D primary
class E,H info
class G error
class F,I warning
2.2 NotificationRequest 核心字段
NotificationRequest 是通知的"身份证",它包含了通知的所有元信息:
// NotificationRequest 关键字段一览
interface NotificationRequest {
id: number; // 通知唯一标识(同一应用内)
content: NotificationContent; // 通知内容(必填)
slotType?: SlotType; // 通知渠道类型
isOngoing?: boolean; // 是否为常驻通知(如音乐播放)
isUnremovable?: boolean; // 是否不可移除
label?: string; // 通知标签
actionButtons?: Array<NotificationActionButton>; // 操作按钮
smallIcon?: image.PixelMap; // 小图标
largeIcon?: image.PixelMap; // 大图标
deliveryTime?: number; // 送达时间
showDeliveryTime?: boolean; // 是否显示送达时间
tapDismissed?: boolean; // 点击后是否自动消失
autoDeletedTime?: number; // 自动消失时间(毫秒)
wantAgent?: WantAgent; // 点击通知后的跳转意图
}
2.3 通知内容类型
HarmonyOS 支持多种通知内容类型,通过 NotificationContent 的不同子类来区分:
| 类型 | 类名 | 适用场景 |
|---|---|---|
| 基本文本 | NotificationContentText |
简短消息提醒 |
| 多行文本 | NotificationContentMultiLine |
消息列表预览 |
| 图片通知 | NotificationContentPicture |
图片类消息 |
2.4 通知生命周期
一条通知从创建到消失,经历以下阶段:
flowchart LR
A[创建 NotificationRequest] --> B[发布通知 publish]
B --> C[通知展示]
C --> D{用户操作}
D -->|点击通知| E[触发 WantAgent 跳转]
D -->|滑动删除| F[通知移除]
D -->|无操作| G[通知停留]
G --> H{自动消失?}
H -->|是| F
H -->|否| G
F --> I[通知生命周期结束]
classDef primary fill:#4CAF50,stroke:#388E3C,color:#fff
classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
classDef error fill:#F44336,stroke:#D32F2F,color:#fff
classDef info fill:#2196F3,stroke:#1976D2,color:#fff
classDef purple fill:#9C27B0,stroke:#7B1FA2,color:#fff
class A,B primary
class C,G info
class D,E,F warning
class I error
三、代码实战
3.1 基础文本通知:最简单的"Hello Notification"
这是最基础的通知发布方式,就像寄一张明信片——内容简单,但能送达。
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct BasicNotificationPage {
@State message: string = '通知基础示例';
/**
* 发布一条基础文本通知
* 这是最简单的通知形式,包含标题和正文
*/
publishBasicNotification(): void {
// 第一步:构建通知请求
const request: notificationManager.NotificationRequest = {
id: 1, // 通知ID,同一应用内唯一
content: {
contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: '收到新消息', // 通知标题
text: '你有一条未读消息,请查收' // 通知正文
}
},
showDeliveryTime: true // 显示通知送达时间
};
// 第二步:发布通知
notificationManager.publish(request).then(() => {
console.info('[通知] 基础通知发布成功');
this.message = '通知发布成功!';
}).catch((err: BusinessError) => {
console.error(`[通知] 发布失败: code=${err.code}, msg=${err.message}`);
this.message = `发布失败: ${err.message}`;
});
}
build() {
Column({ space: 20 }) {
Text(this.message)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Button('发布基础通知')
.width('80%')
.height(50)
.onClick(() => this.publishBasicNotification())
}
.width('100%')
.height('100%')
.justifyContent(Fenter)
.padding(20)
}
}
3.2 通知发布与取消:完整的生命周期管理
实际开发中,我们不仅要会发通知,还要会"撤回"通知。就像发消息可以撤回一样,通知也有取消机制。
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { WantAgent, wantAgent } from '@kit.AbilityKit';
@Entry
@Component
struct NotificationLifecyclePage {
@State statusText: string = '等待操作...';
@State notificationId: number = 100; // 通知ID
/**
* 创建 WantAgent,用于点击通知后跳转到指定页面
* WantAgent 是通知与Ability之间的"桥梁"
*/
async createWantAgent(): Promise<WantAgent> {
const wantAgentInfo: wantAgent.WantAgentInfo = {
wants: [
{
bundleName: 'com.example.notificationdemo',
abilityName: 'EntryAbility'
}
],
requestCode: 0,
operationType: wantAgent.OperationType.START_ABILITY,
wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG]
};
return wantAgent.getWantAgent(wantAgentInfo);
}
/**
* 发布带跳转功能的通知
* 点击通知后会跳转回应用
*/
async publishNotificationWithAction(): Promise<void> {
try {
// 创建跳转意图
const agent = await this.createWantAgent();
// 构建通知请求
const request: notificationManager.NotificationRequest = {
id: this.notificationId,
content: {
contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: '任务完成提醒',
text: '你的后台任务已处理完毕,点击查看详情'
}
},
wantAgent: agent, // 绑定跳转意图
showDeliveryTime: true, // 显示送达时间
tapDismissed: true // 点击后自动消失
};
await notificationManager.publish(request);
this.statusText = `通知 #${this.notificationId} 已发布`;
console.info('[通知] 带跳转的通知发布成功');
} catch (err) {
const error = err as BusinessError;
this.statusText = `发布失败: ${error.message}`;
console.error(`[通知] 发布失败: ${error.code}`);
}
}
/**
* 取消指定ID的通知
* 取消后通知将从通知中心移除
*/
async cancelNotification(): Promise<void> {
try {
await notificationManager.cancel(this.notificationId);
this.statusText = `通知 #${this.notificationId} 已取消`;
console.info('[通知] 通知取消成功');
} catch (err) {
const error = err as BusinessError;
this.statusText = `取消失败: ${error.message}`;
console.error(`[通知] 取消失败: ${error.code}`);
}
}
/**
* 取消所有通知
* 批量清理应用发出的所有通知
*/
async cancelAllNotifications(): Promise<void> {
try {
await notificationManager.cancelAll();
this.statusText = '所有通知已清除';
console.info('[通知] 全部通知取消成功');
} catch (err) {
const error = err as BusinessError;
this.statusText = `清除失败: ${error.message}`;
}
}
build() {
Column({ space: 16 }) {
Text('通知生命周期管理')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Text(this.statusText)
.fontSize(16)
.fontColor('#666666')
.textAlign(TextAlign.Center)
.width('90%')
.padding(12)
.borderRadius(8)
.backgroundColor('#F5F5F5')
Button('发布通知(带跳转)')
.width('80%')
.height(50)
.backgroundColor('#4CAF50')
.onClick(() => this.publishNotificationWithAction())
Button('取消指定通知')
.width('80%')
.height(50)
.backgroundColor('#FF9800')
.onClick(() => this.cancelNotification())
Button('取消所有通知')
.width('80%')
.height(50)
.backgroundColor('#F44336')
.onClick(() => this.cancelAllNotifications())
}
.width('100%')
.height('100%')
.justifyContent(Fenter)
.padding(20)
}
}
3.3 带操作按钮的通知:交互式通知
有时候,我们希望用户不打开应用就能对通知做出响应——比如直接在通知上"标记已读"或"删除"。这就是操作按钮的用武之地。
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { WantAgent, wantAgent } from '@kit.AbilityKit';
@Entry
@Component
struct ActionButtonNotificationPage {
@State actionLog: string = '暂无操作记录';
/**
* 创建 WantAgent,携带操作类型参数
* 通过 URI 参数区分不同的按钮操作
*/
async createActionWantAgent(action: string): Promise<WantAgent> {
const wantAgentInfo: wantAgent.WantAgentInfo = {
wants: [
{
bundleName: 'com.example.notificationdemo',
abilityName: 'EntryAbility',
parameters: {
actionType: action // 传递操作类型:mark_read / delete / reply
}
}
],
requestCode: 0,
operationType: wantAgent.OperationType.START_ABILITY,
wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG]
};
return wantAgent.getWantAgent(wantAgentInfo);
}
/**
* 发布带操作按钮的通知
* 用户可以直接在通知上执行操作,无需打开应用
*/
async publishNotificationWithButtons(): Promise<void> {
try {
// 为每个按钮创建不同的 WantAgent
const markReadAgent = await this.createActionWantAgent('mark_read');
const deleteAgent = await this.createActionWantAgent('delete');
const replyAgent = await this.createActionWantAgent('reply');
// 构建操作按钮
const actionButtons: Array<notificationManager.NotificationActionButton> = [
{
title: '标记已读',
wantAgent: markReadAgent
},
{
title: '删除',
wantAgent: deleteAgent
},
{
title: '回复',
wantAgent: replyAgent
}
];
// 构建通知请求
const request: notificationManager.NotificationRequest = {
id: 200,
content: {
contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: '新邮件提醒',
text: '张三发来一封邮件:关于下周项目评审的安排'
}
},
actionButtons: actionButtons, // 绑定操作按钮
showDeliveryTime: true,
tapDismissed: false // 点击通知本身不消失,需要用户手动操作
};
await notificationManager.publish(request);
this.actionLog = '带操作按钮的通知已发布\n请在通知中心查看';
console.info('[通知] 带按钮的通知发布成功');
} catch (err) {
const error = err as BusinessError;
this.actionLog = `发布失败: ${error.message}`;
console.error(`[通知] 发布失败: ${error.code}`);
}
}
/**
* 发布常驻通知(如音乐播放器)
* 常驻通知不会因滑动而消失,适合需要持续展示的场景
*/
async publishOngoingNotification(): Promise<void> {
try {
const request: notificationManager.NotificationRequest = {
id: 300,
content: {
contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: '正在播放音乐',
text: '周杰伦 - 晴天'
}
},
isOngoing: true, // 标记为常驻通知
isUnremovable: false, // 用户仍可通过设置关闭
showDeliveryTime: false
};
await notificationManager.publish(request);
this.actionLog = '常驻通知已发布\n模拟音乐播放器通知';
console.info('[通知] 常驻通知发布成功');
} catch (err) {
const error = err as BusinessError;
this.actionLog = `发布失败: ${error.message}`;
}
}
build() {
Scroll() {
Column({ space: 16 }) {
Text('交互式通知')
.fontSize(24)
.fontWeight(FontWeight.Bold)
// 操作日志展示区
Text(this.actionLog)
.fontSize(14)
.fontColor('#333333')
.textAlign(TextAlign.Start)
.width('90%')
.padding(16)
.borderRadius(12)
.backgroundColor('#F0F4F8')
.minHeight(100)
Button('发布带按钮的通知')
.width('80%')
.height(50)
.backgroundColor('#2196F3')
.onClick(() => this.publishNotificationWithButtons())
Button('发布常驻通知(音乐播放器)')
.width('80%')
.height(50)
.backgroundColor('#9C27B0')
.onClick(() => this.publishOngoingNotification())
Button('清除所有通知')
.width('80%')
.height(50)
.backgroundColor('#F44336')
.onClick(async () => {
await notificationManager.cancelAll();
this.actionLog = '所有通知已清除';
})
}
.width('100%')
.padding(20)
}
.width('100%')
.height('100%')
}
}
3.4 通知权限检查与请求
通知不是"想发就能发"的,用户可能关闭了通知权限。在发布通知前,务必检查权限状态。
import { notificationManager } from '@kit.NotificationKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct NotificationPermissionPage {
@State permissionStatus: string = '检查中...';
@State notificationEnabled: boolean = false;
/**
* 检查通知权限状态
* 在发布通知前必须检查,否则可能静默失败
*/
async checkNotificationPermission(): Promise<void> {
try {
// 检查通知是否已启用
const enabled = await notificationManager.isNotificationEnabled();
this.notificationEnabled = enabled;
this.permissionStatus = enabled ? '通知权限:已开启 ✅' : '通知权限:未开启 ❌';
console.info(`[通知权限] 状态: ${enabled}`);
} catch (err) {
const error = err as BusinessError;
this.permissionStatus = `检查失败: ${error.message}`;
console.error(`[通知权限] 检查失败: ${error.code}`);
}
}
/**
* 请求通知权限
* 如果用户未授权,引导用户到系统设置页面开启
*/
async requestNotificationPermission(): Promise<void> {
try {
// 尝试请求通知权限
await notificationManager.requestEnableNotification();
this.permissionStatus = '通知权限:已开启 ✅';
this.notificationEnabled = true;
console.info('[通知权限] 权限请求成功');
} catch (err) {
const error = err as BusinessError;
// 用户拒绝或系统不支持时,引导到系统设置
this.permissionStatus = '权限被拒绝,请手动到设置中开启';
console.warn(`[通知权限] 请求失败: ${error.code}`);
}
}
/**
* 安全地发布通知(带权限检查)
* 推荐在所有通知发布前都进行权限检查
*/
async safePublishNotification(): Promise<void> {
// 先检查权限
const enabled = await notificationManager.isNotificationEnabled();
if (!enabled) {
this.permissionStatus = '请先开启通知权限';
return;
}
// 权限OK,发布通知
const request: notificationManager.NotificationRequest = {
id: 400,
content: {
contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: '权限检查通过',
text: '这是一条安全发布的通知'
}
},
showDeliveryTime: true
};
try {
await notificationManager.publish(request);
this.permissionStatus = '通知发布成功!';
} catch (err) {
const error = err as BusinessError;
this.permissionStatus = `发布失败: ${error.message}`;
}
}
aboutToAppear(): void {
// 页面加载时自动检查权限
this.checkNotificationPermission();
}
build() {
Column({ space: 16 }) {
Text('通知权限管理')
.fontSize(24)
.fontWeight(FontWeight.Bold)
// 权限状态展示
Row() {
Text(this.permissionStatus)
.fontSize(18)
.fontWeight(FontWeight.Medium)
}
.width('90%')
.padding(20)
.borderRadius(12)
.backgroundColor(this.notificationEnabled ? '#E8F5E9' : '#FFF3E0')
.justifyContent(FlexAlign.Center)
Button('检查通知权限')
.width('80%')
.height(50)
.backgroundColor('#2196F3')
.onClick(() => this.checkNotificationPermission())
Button('请求通知权限')
.width('80%')
.height(50)
.backgroundColor('#FF9800')
.onClick(() => this.requestNotificationPermission())
Button('安全发布通知')
.width('80%')
.height(50)
.backgroundColor('#4CAF50')
.onClick(() => this.safePublishNotification())
}
.width('100%')
.height('100%')
.justifyContent(Fenter)
.padding(20)
}
}
四、踩坑与注意事项
4.1 通知ID冲突问题
问题:同一应用内,如果两条通知使用了相同的 id,后发布的通知会覆盖前一条。
// ❌ 错误示范:两条通知用了同一个ID
const request1 = { id: 1, content: { ... } }; // 通知A
const request2 = { id: 1, content: { ... } }; // 通知B,会覆盖A!
// ✅ 正确做法:每条通知使用不同的ID
const request1 = { id: 1001, content: { ... } };
const request2 = { id: 1002, content: { ... } };
建议:使用自增ID或基于业务逻辑的ID生成策略(如 orderId * 1000 + type)。
4.2 通知权限静默失败
问题:在用户未授权的情况下调用 publish(),不会抛出异常,但通知不会展示。这就像往一个关着的信箱里塞信——信是塞进去了,但收信人看不到。
解决方案:每次发布通知前,务必调用 isNotificationEnabled() 检查权限状态。
4.3 WantAgent 创建失败
问题:WantAgent 中的 bundleName 或 abilityName 写错时,通知能正常发布,但点击后无法跳转,且不会报错。
建议:使用常量管理包名和Ability名,避免硬编码:
// ✅ 推荐做法:常量管理
const APP_BUNDLE_NAME = 'com.example.notificationdemo';
const ENTRY_ABILITY_NAME = 'EntryAbility';
4.4 通知数量限制
系统对应用的通知数量有上限(通常为50条)。超过限制后,最早的通知会被自动移除。如果你的应用需要发送大量通知(如聊天消息),建议实现通知合并逻辑。
4.5 主线程调用
notificationManager 的所有 API 都是异步的,但必须在主线程调用。如果在 Worker 或子线程中调用,会抛出异常。
五、HarmonyOS 6 适配
5.1 API 变更
| 变更项 | HarmonyOS 5 | HarmonyOS 6 |
|---|---|---|
| 模块名 | @ohos.notificationManager |
@ohos.notificationManager(兼容) |
| 权限模型 | ohos.permission.NOTIFICATION_CONTROLLER |
新增细粒度权限控制 |
| 通知ID范围 | 0 ~ 2147483647 | 新增分组ID机制 |
| WantAgent | 基础跳转 | 支持 UIAbilityContext 自动注入 |
5.2 迁移指南
- 权限声明:HarmonyOS 6 对通知权限做了更细粒度的划分,需要在
module.json5中声明:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.NOTIFICATION_CONTROLLER"
}
]
}
}
-
API 兼容性:HarmonyOS 6 保持了 API 12 的接口兼容性,现有代码无需修改即可运行。但建议使用新的分组通知 API 来优化用户体验。
-
通知样式增强:HarmonyOS 6 新增了更多通知模板类型,建议在迁移时评估是否使用新样式。
六、总结
mindmap
root((通知基础))
核心API
notificationManager.publish
notificationManager.cancel
notificationManager.cancelAll
notificationManager.isNotificationEnabled
核心对象
NotificationRequest
id 唯一标识
content 通知内容
wantAgent 跳转意图
actionButtons 操作按钮
NotificationContent
BASIC_TEXT 基本文本
MULTI_LINE 多行文本
PICTURE 图片通知
关键流程
检查权限
构建请求
发布通知
取消通知
注意事项
通知ID唯一性
权限静默失败
WantAgent正确配置
通知数量限制
主线程调用
| 知识点 | 要点 |
|---|---|
| 通知发布 | 通过 NotificationRequest 构建内容,调用 publish() 发布 |
| 通知取消 | cancel(id) 取消单条,cancelAll() 取消全部 |
| 操作按钮 | actionButtons 数组,每个按钮绑定独立的 WantAgent |
| 常驻通知 | isOngoing: true,适合音乐播放等持续场景 |
| 权限检查 | 发布前必须调用 isNotificationEnabled() 检查 |
| WantAgent | 通知点击跳转的"桥梁",注意包名和Ability名正确性 |
通知是应用与用户沟通的桥梁,掌握基础API只是第一步。下一篇我们将深入通知渠道,看看如何通过渠道管理来优化通知的展示策略和用户体验。
- 点赞
- 收藏
- 关注作者
评论(0)