HarmonyOS APP中@ohos.notification、NotificationRequest、通知发布与取消

举报
Jack20 发表于 2026/06/20 13:41:55 2026/06/20
【摘要】 HarmonyOS APP中@ohos.notification、NotificationRequest、通知发布与取消📌 核心要点:掌握 HarmonyOS 通知体系的核心 API,从 NotificationRequest 构建到通知的发布与取消,打通应用与用户之间的"最后一公里"通信。 一、背景与动机你有没有这样的经历?手机静音放在桌上,错过了一条重要的快递通知;或者某个 App ...

HarmonyOS APP中@ohos.notification、NotificationRequest、通知发布与取消

📌 核心要点:掌握 HarmonyOS 通知体系的核心 API,从 NotificationRequest 构建到通知的发布与取消,打通应用与用户之间的"最后一公里"通信。


一、背景与动机

你有没有这样的经历?手机静音放在桌上,错过了一条重要的快递通知;或者某个 App 疯狂弹通知,让你恨不得直接卸载它。通知,这个看似简单的功能,其实是应用和用户之间最直接的沟通桥梁。

在 HarmonyOS 的世界里,通知不仅仅是"弹个框"那么简单。它是一套完整的消息分发体系——从构建通知内容、选择通知渠道、到最终展示在用户的通知中心,每一个环节都有精细的 API 支持。而这一切的起点,就是 @ohos.notificationManager 模块。

打个比方:通知就像快递系统。NotificationRequest 是你要寄的包裹,通知渠道是快递公司的不同配送线路,而 notificationManager 就是那个快递柜——负责把包裹投递到用户手中。理解了这个类比,后面的 API 学习就会顺畅很多。


二、核心原理

2.1 通知体系架构

HarmonyOS 的通知体系由三层构成:

层级 职责 关键 API
应用层 构建通知内容、发布/取消通知 notificationManager.publish() / cancel()
框架层 通知路由、渠道管理、优先级排序 NotificationSlotNotificationRequest
系统层 通知展示、用户交互、权限管控 通知中心、状态栏、锁屏
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 中的 bundleNameabilityName 写错时,通知能正常发布,但点击后无法跳转,且不会报错。

建议:使用常量管理包名和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 迁移指南

  1. 权限声明:HarmonyOS 6 对通知权限做了更细粒度的划分,需要在 module.json5 中声明:
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.NOTIFICATION_CONTROLLER"
      }
    ]
  }
}
  1. API 兼容性:HarmonyOS 6 保持了 API 12 的接口兼容性,现有代码无需修改即可运行。但建议使用新的分组通知 API 来优化用户体验。

  2. 通知样式增强: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只是第一步。下一篇我们将深入通知渠道,看看如何通过渠道管理来优化通知的展示策略和用户体验。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。