
引言
通知推送(Notification)是现代移动应用与用户保持连接的核心渠道。通过本地通知,应用可以在特定时机向用户推送重要信息,如节气提醒、每日知识、系统公告等,从而提升用户活跃度和留存率。在HarmonyOS应用开发中,@ohos.notificationManager 模块提供了完整的本地通知管理能力,包括权限申请、通知发送、通知取消、分类管理等。
一个完善的通知推送系统应该具备以下特点:
- 权限合规:正确申请和管理通知权限
- 类型区分:支持多种通知类型(节气提醒、每日推送、系统公告)
- 用户可控:提供开关设置,尊重用户选择
- 内容丰富:支持标题、正文、附加信息等完整结构
- 状态可查:能够查询和清理通知
- 错误处理:完善的异常捕获和降级策略
本文为实战总结版本 ,所有代码均经过项目验证编译通过,包含完整的通知服务封装、权限处理、设置页面集成等实战内容。
学习目标
完成本文后,你将能够:
- ✅ 理解HarmonyOS通知系统的架构与原理
- ✅ 正确配置通知相关权限
- ✅ 实现通知服务的单例模式封装
- ✅ 发送多种类型的本地通知(文本通知)
- ✅ 实现通知权限的检查与引导
- ✅ 集成通知设置页面与持久化存储
- ✅ 实现通知的取消与批量清理
- ✅ 掌握通知ID生成与分组策略
需求分析
功能模块设计
| 模块 | 功能描述 | 技术要点 |
|---|---|---|
| 通知服务 | 管理所有通知操作 | 单例模式、notificationManager API |
| 权限管理 | 检查和请求通知权限 | isNotificationEnabled、系统设置引导 |
| 通知发送 | 发送各类本地通知 | publish()、NotificationRequest 构建 |
| 节气提醒 | 节气当天自动推送 | 定时触发 + 类型判断 |
| 每日推送 | 每日知识定时推送 | 开关控制 + 内容截断 |
| 设置页面 | 用户自定义通知偏好 | Toggle组件 + Preferences存储 |
| 通知管理 | 取消/清除通知 | cancel() / cancelAll() |
支持的通知类型
| 通知类型 | 标识码 | 触发场景 | 示例 |
|---|---|---|---|
| 节气提醒 | holiday |
当日为二十四节气之一 | "今日立夏" |
| 每日知识 | daily |
每天定时推送 | "今日小满:麦粒渐满..." |
| 系统公告 | system |
应用更新、活动通知 | "新版本已发布" |
通知设置项
| 设置项 | 默认值 | 说明 |
|---|---|---|
| pushEnabled | true | 全局推送总开关 |
| soundEnabled | true | 通知声音 |
| vibrationEnabled | true | 震动反馈 |
| dailyReminder | true | 每日知识推送 |
| holidayNotification | true | 节气当天提醒 |
核心实现
步骤1: 配置模块权限
在 module.json5 中声明通知相关权限:
json5
// entry/src/main/module.json5
{
"module": {
// ... 其他配置
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
// 通知控制器权限:用于发布和管理本地通知
"name": "ohos.permission.NOTIFICATION_CONTROLLER"
}
]
}
}
权限说明
| 权限名称 | 用途 | 必要性 |
|---|---|---|
ohos.permission.INTERNET |
网络访问(项目基础依赖) | 必需 |
ohos.permission.NOTIFICATION_CONTROLLER |
发布和管理本地通知 | 本地通知必需 |
注意 :HarmonyOS 中通知权限属于敏感权限 ,需要在系统设置中由用户手动开启。应用无法直接通过代码强制开启,只能通过
isNotificationEnabled()检测状态并引导用户前往设置。
步骤2: 设计通知数据模型
typescript
// services/NotificationService.ets
/** 通知类型枚举 */
export enum NotificationType {
/** 节气提醒 - 当天为二十四节气时触发 */
HOLIDAY = 'holiday',
/** 每日知识推送 - 每天定时推送节气相关知识 */
DAILY = 'daily',
/** 系统公告 - 应用更新、活动通知等 */
SYSTEM = 'system'
}
/** 通知配置接口 */
export interface NotificationConfig {
type: NotificationType; // 通知类型
title: string; // 通知标题
content: string; // 通知正文
extraInfo?: Record<string, string>; // 附加信息(用于点击跳转等)
}
设计要点
1. 通知类型枚举
使用字符串枚举定义三种通知类型,便于:
- 在代码中通过类型进行条件分支
- 作为通知的
groupName进行分组显示 - 与设置页面的开关一一对应
typescript
export enum NotificationType {
HOLIDAY = 'holiday', // 节气提醒
DAILY = 'daily', // 每日推送
SYSTEM = 'system' // 系统公告
}
2. 通知配置接口
统一的配置接口确保所有通知遵循相同的数据结构:
typescript
export interface NotificationConfig {
type: NotificationType; // 分类标识
title: string; // 标题(显示在通知栏顶部)
content: string; // 正文(标题下方的内容)
extraInfo?: Record<string, string>; // 扩展字段,如跳转参数
}
步骤3: 实现通知服务核心类
typescript
// services/NotificationService.ets
import notificationManager from '@ohos.notificationManager';
import { common } from '@kit.AbilityKit';
import promptAction from '@ohos.promptAction';
import { StorageService } from './StorageService';
/**
* 通知推送服务(单例模式)
*
* 职责:
* 1. 管理通知权限的检查与引导
* 2. 封装 notificationManager 的 publish/cancel 操作
* 3. 提供语义化的高层API(sendHolidayNotification 等)
* 4. 结合 StorageService 实现用户设置的读取与过滤
*
* 使用方式:
* ```typescript
* const service = NotificationService.getInstance();
* await service.init(context, storageService);
* await service.sendHolidayNotification('立夏', '2026-05-05');
* ```
*/
export class NotificationService {
private static instance: NotificationService;
private context: common.Context | null = null;
private storageService: StorageService | null = null;
private isInitialized: boolean = false;
private constructor() {}
/** 获取单例实例 */
static getInstance(): NotificationService {
if (!NotificationService.instance) {
NotificationService.instance = new NotificationService();
}
return NotificationService.instance;
}
/**
* 初始化服务
*
* 应在 EntryAbility 的 onCreate 或首页 aboutToAppear 中调用。
* 会自动检测通知权限状态并在未授权时给出提示。
*
* @param context 应用上下文(UIAbilityContext)
* @param storageService 存储服务实例(用于读取通知设置)
*/
async init(context: common.Context, storageService: StorageService): Promise<void> {
this.context = context;
this.storageService = storageService;
try {
const enabled = await this.checkNotificationEnabled();
if (!enabled) {
// 首次启动或权限被关闭时,引导用户
await this.requestNotificationPermission();
}
this.isInitialized = true;
console.info('NotificationService 初始化完成');
} catch (error) {
console.error('NotificationService 初始化失败:', error);
}
}
/**
* 检查当前通知是否已启用
*
* @returns true=已开启,false=未开启
*/
async checkNotificationEnabled(): Promise<boolean> {
try {
return await notificationManager.isNotificationEnabled();
} catch (error) {
console.error('检查通知状态失败:', error);
return false;
}
}
/**
* 请求通知权限
*
* HarmonyOS 中通知权限需用户在系统设置中手动开启,
* 此方法会弹出 Toast 提示用户前往设置页面。
*/
async requestNotificationPermission(): Promise<void> {
try {
const isEnabled = await notificationManager.isNotificationEnabled();
if (isEnabled) {
return; // 已开启则直接返回
}
promptAction.showToast({
message: '请在系统设置中开启通知权限',
duration: 3000
});
} catch (error) {
console.error('请求通知权限失败:', error);
}
}
// ========== 核心发送方法 ==========
/**
* 发送本地通知(通用方法)
*
* 内部流程:
* 1. 检查服务是否初始化
* 2. 读取全局推送开关(pushEnabled)
* 3. 根据通知类型读取对应开关(holidayNotification/dailyReminder)
* 4. 构建 NotificationRequest 并调用 publish()
*
* @param config 通知配置
* @returns 是否发送成功
*/
async sendNotification(config: NotificationConfig): Promise<boolean> {
try {
if (!this.isInitialized) {
console.warn('NotificationService 未初始化');
return false;
}
// 检查全局推送开关
if (this.storageService) {
const pushEnabled = await this.storageService.getNotificationSetting('pushEnabled', true);
if (!pushEnabled) {
console.info('推送通知已关闭,跳过发送');
return false;
}
}
// 根据类型检查对应开关
if (config.type === NotificationType.HOLIDAY && this.storageService) {
const holidayNotif = await this.storageService.getNotificationSetting('holidayNotification', true);
if (!holidayNotif) return false;
}
if (config.type === NotificationType.DAILY && this.storageService) {
const dailyReminder = await this.storageService.getNotificationSetting('dailyReminder', true);
if (!dailyReminder) return false;
}
// 构建并发送通知
const request: notificationManager.NotificationRequest = {
id: this.generateNotificationId(config.type),
content: this.buildContent(config),
deliveryTime: Date.now(),
tapDismissed: true,
classification: notificationManager.Classification.SOCIAL,
groupName: config.type
};
await notificationManager.publish(request);
console.info(`通知发送成功: [${config.type}] ${config.title}`);
return true;
} catch (error) {
console.error(`通知发送失败:`, error);
return false;
}
}
// ========== 便捷发送方法 ==========
/**
* 发送节气提醒通知
*
* @param solarName 节气名称(如"立夏"、"小满")
* @param date 日期字符串(如"2026-05-05")
* @param description 可选的节气描述
*/
async sendHolidayNotification(
solarName: string,
date: string,
description?: string
): Promise<boolean> {
const config: NotificationConfig = {
type: NotificationType.HOLIDAY,
title: `今日${solarName}`,
content: description || `${date},${solarName}到了!点击查看节气详情。`,
extraInfo: { solarName, date, type: 'holiday' }
};
return this.sendNotification(config);
}
/**
* 发送每日知识推送
*
* @param title 知识标题
* @param content 知识内容(超过50字符自动截断)
*/
async sendDailyKnowledge(title: string, content: string): Promise<boolean> {
const config: NotificationConfig = {
type: NotificationType.DAILY,
title: title,
content: content.length > 50 ? content.substring(0, 50) + '...' : content,
extraInfo: { type: 'daily' }
};
return this.sendNotification(config);
}
/**
* 发送系统公告
*
* @param title 公告标题
* @param content 公告内容
*/
async sendSystemNotice(title: string, content: string): Promise<boolean> {
const config: NotificationConfig = {
type: NotificationType.SYSTEM,
title: title,
content: content,
extraInfo: { type: 'system' }
};
return this.sendNotification(config);
}
// ========== 通知管理方法 ==========
/**
* 取消指定ID的通知
*
* @param id 通知ID(publish 时生成的 id)
*/
async cancelNotification(id: number): Promise<void> {
try {
await notificationManager.cancel(id);
console.info(`通知已取消: id=${id}`);
} catch (error) {
console.error('取消通知失败:', error);
}
}
/**
* 清除本应用的所有通知
*/
async cancelAllNotifications(): Promise<void> {
try {
await notificationManager.cancelAll();
console.info('所有通知已清除');
} catch (error) {
console.error('清除所有通知失败:', error);
}
}
/**
* 获取当前活跃通知数量
*
* @returns 未消除的通知数量
*/
async getActiveNotificationCount(): Promise<number> {
try {
const notifications = await notificationManager.getActiveNotifications();
return notifications.length;
} catch (error) {
console.error('获取活跃通知数量失败:', error);
return 0;
}
}
// ========== 私有辅助方法 ==========
/**
* 构建基础文本通知内容
*/
private buildContent(config: NotificationConfig): notificationManager.NotificationContent {
return {
contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: config.title,
text: config.content,
additionalText: '节气通'
}
};
}
/**
* 生成唯一通知ID
*
* 策略:类型前缀 + 时间戳后4位
* - holiday: 1000~1999
* - daily: 2000~2999
* - system: 3000~3999
*/
private generateNotificationId(type: NotificationType): number {
const typeHash: Record<NotificationType, number> = {
[NotificationType.HOLIDAY]: 1000,
[NotificationType.DAILY]: 2000,
[NotificationType.SYSTEM]: 3000
};
const baseId = typeHash[type] || 1000;
return baseId + (Date.now() % 10000);
}
/** 销毁服务 */
destroy(): void {
this.context = null;
this.storageService = null;
this.isInitialized = false;
}
}
核心技术点解析
1. 单例模式
全局唯一的 NotificationService 实例,避免重复初始化:
typescript
private static instance: NotificationService;
private constructor() {}
static getInstance(): NotificationService {
if (!NotificationService.instance) {
NotificationService.instance = new NotificationService();
}
return NotificationService.instance;
}
2. 权限检查流程
应用启动 → init()
→ checkNotificationEnabled()
→ 已开启? ──是──→ 完成
│
否
↓
requestNotificationPermission()
→ 弹出 Toast 引导用户去设置
3. 通知发送前的双重过滤
每次发送通知前,依次检查两级开关:
typescript
// 第一级:全局推送开关
const pushEnabled = await storageService.getNotificationSetting('pushEnabled', true);
// 第二级:类型专属开关(仅对 holiday 和 daily 生效)
if (type === HOLIDAY) {
const holidayOn = await storageService.getNotificationSetting('holidayNotification', true);
}
这种设计让用户既能一键关闭所有通知,也能精细化控制每种通知类型。
4. NotificationRequest 关键字段
| 字段 | 说明 | 本文取值 |
|---|---|---|
id |
通知唯一标识 | 基于类型+时间戳生成 |
contentType |
内容类型 | NOTIFICATION_CONTENT_BASIC_TEXT |
deliveryTime |
投递时间 | Date.now()(立即投递) |
tapDismissed |
点击后是否消失 | true |
classification |
通知分类 | SOCIAL(社交类) |
groupName |
分组名称 | 通知类型字符串 |
5. ID生成策略
使用类型前缀 + 时间戳后4位,保证:
- 不同类型的通知不会ID冲突
- 同一类型短时间内多次推送也有不同ID
- ID范围固定,便于管理和调试
步骤4: 存储层扩展
在 StorageService 中添加通知设置相关的存取方法:
typescript
// services/StorageService.ets(已有代码)
/** 通知设置存储键前缀 */
const NOTIFICATION_KEY = 'notification_settings';
/**
* 获取单个通知设置项
*
* @param key 设置键名(如 'pushEnabled'、'dailyReminder')
* @param defaultValue 未找到时的默认值
* @returns 设置值
*/
async getNotificationSetting(key: string, defaultValue: boolean): Promise<boolean> {
try {
const prefs = await this.getPreferences();
const fullKey = `${NOTIFICATION_KEY}_${key}`;
return prefs.get(fullKey, defaultValue) as boolean;
} catch (error) {
console.error(`获取通知设置 ${key} 失败:`, error);
return defaultValue;
}
}
/**
* 保存单个通知设置项
*
* @param key 设置键名
* @param value 设置值
*/
async setNotificationSetting(key: string, value: boolean): Promise<void> {
try {
const prefs = await this.getPreferences();
const fullKey = `${NOTIFICATION_KEY}_${key}`;
await prefs.put(fullKey, value);
await prefs.flush();
} catch (error) {
console.error(`保存通知设置 ${key} 失败:`, error);
}
}
/**
* 获取所有通知设置
*
* @returns 所有设置项的键值对
*/
async getAllNotificationSettings(): Promise<Record<string, boolean>> {
const keys = ['pushEnabled', 'soundEnabled', 'vibrationEnabled',
'dailyReminder', 'holidayNotification'];
const result: Record<string, boolean> = {};
for (const key of keys) {
result[key] = await this.getNotificationSetting(key, true);
}
return result;
}
存储键命名规范
| 完整键名 | 对应设置 | 默认值 |
|---|---|---|
notification_settings_pushEnabled |
全局推送开关 | true |
notification_settings_soundEnabled |
通知声音 | true |
notification_settings_vibrationEnabled |
震动反馈 | true |
notification_settings_dailyReminder |
每日提醒 | true |
notification_settings_holidayNotification |
节气提醒 | true |
使用 notification_settings_ 前缀统一命名空间,避免与其他功能的数据冲突。
步骤5: 通知设置页面
typescript
// pages/NotificationSettings.ets
import router from '@ohos.router';
import { common } from '@kit.AbilityKit';
import promptAction from '@ohos.promptAction';
import { StorageService } from '../services/StorageService';
import { I18nService } from '../services/I18nService';
@Entry
@Component
export struct NotificationSettings {
// i18n 动态文本变量
@State pageTitle: string = '通知设置';
@State pushGroupTitle: string = '推送通知';
@State pushTitle: string = '允许推送';
@State pushDesc: string = '接收应用推送消息';
@State soundTitle: string = '通知声音';
@State soundDesc: string = '收到通知时播放声音';
@State vibrationTitle: string = '震动反馈';
@State vibrationDesc: string = '收到通知时震动提醒';
@State reminderGroupTitle: string = '提醒设置';
@State dailyReminderTitle: string = '每日提醒';
@State dailyReminderDesc: string = '每天定时推送节气知识';
@State holidayNotifTitle: string = '节气提醒';
@State holidayNotifDesc: string = '节气当天发送祝福消息';
// 开关状态
@State pushEnabled: boolean = true;
@State soundEnabled: boolean = true;
@State vibrationEnabled: boolean = true;
@State dailyReminder: boolean = true;
@State holidayNotification: boolean = true;
private storageService: StorageService | null = null;
private i18nService: I18nService | null = null;
async aboutToAppear() {
const context = getContext(this) as common.UIAbilityContext;
// 初始化国际化服务
this.i18nService = I18nService.getInstance();
await this.i18nService.init(context, undefined);
this.loadStrings();
this.i18nService.addLanguageChangeListener(() => {
this.loadStrings();
});
// 初始化存储服务并加载设置
this.storageService = new StorageService(context);
await this.storageService.init();
await this.loadSettings();
}
/** 从存储中加载所有设置项 */
async loadSettings() {
if (!this.storageService) return;
try {
this.pushEnabled = await this.storageService.getNotificationSetting('pushEnabled', true);
this.soundEnabled = await this.storageService.getNotificationSetting('soundEnabled', true);
this.vibrationEnabled = await this.storageService.getNotificationSetting('vibrationEnabled', true);
this.dailyReminder = await this.storageService.getNotificationSetting('dailyReminder', true);
this.holidayNotification = await this.storageService.getNotificationSetting('holidayNotification', true);
} catch (error) {
console.error('加载通知设置失败:', error);
}
}
/** 保存单个设置项到存储 */
async saveSetting(key: string, value: boolean) {
if (!this.storageService) return;
try {
await this.storageService.setNotificationSetting(key, value);
} catch (error) {
console.error('保存通知设置失败:', error);
}
}
/** 加载国际化文本 */
loadStrings(): void {
const service = this.i18nService;
if (!service) return;
this.pageTitle = service.getString('notification_settings');
this.pushGroupTitle = service.getString('push_notifications');
this.pushTitle = service.getString('allow_push');
this.pushDesc = service.getString('allow_push_desc');
this.soundTitle = service.getString('notification_sound');
this.soundDesc = service.getString('notification_sound_desc');
this.vibrationTitle = service.getString('vibration_feedback');
this.vibrationDesc = service.getString('vibration_feedback_desc');
this.reminderGroupTitle = service.getString('reminder_settings');
this.dailyReminderTitle = service.getString('daily_reminder');
this.dailyReminderDesc = service.getString('daily_reminder_desc');
this.holidayNotifTitle = service.getString('holiday_notification');
this.holidayNotifDesc = service.getString('holiday_notification_desc');
}
build() {
Column() {
// 导航栏
Row() {
Image($r('app.media.icon_back'))
.width(24)
.height(24)
.fillColor('#333333')
.onClick(() => router.back())
Text(this.pageTitle)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.layoutWeight(1)
.textAlign(TextAlign.Center)
Blank().width(24)
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor('#FFFFFF')
// 设置列表
Scroll() {
Column({ space: 16 }) {
// 推送通知组
this.buildSwitchGroup(this.pushGroupTitle, [
{ title: this.pushTitle, desc: this.pushDesc, key: 'pushEnabled', value: this.pushEnabled },
{ title: this.soundTitle, desc: this.soundDesc, key: 'soundEnabled', value: this.soundEnabled },
{ title: this.vibrationTitle, desc: this.vibrationDesc, key: 'vibrationEnabled', value: this.vibrationEnabled }
])
// 提醒设置组
this.buildSwitchGroup(this.reminderGroupTitle, [
{ title: this.dailyReminderTitle, desc: this.dailyReminderDesc, key: 'dailyReminder', value: this.dailyReminder },
{ title: this.holidayNotifTitle, desc: this.holidayNotifDesc, key: 'holidayNotification', value: this.holidayNotification }
])
// 底部说明
Column({ space: 8 }) {
Text('通知说明')
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
Text('开启通知后,您将及时收到节气相关的提醒和推送消息。您可以根据喜好自定义通知类型。')
.fontSize(13)
.fontColor('#999999')
.lineHeight(20)
}
.padding(16)
.backgroundColor('#F8F7F2')
.borderRadius(12)
.width('100%')
}
.padding({ left: 16, right: 16, top: 16, bottom: 40 })
}
.width('100%')
.height('100%')
.scrollBar(BarState.Off)
.backgroundColor('#F5F5F5')
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
@Builder
buildSwitchGroup(title: string, items: { title: string; desc: string; key: string; value: boolean }[]) {
Column({ space: 0 }) {
Text(title)
.fontSize(14)
.fontColor('#999999')
.width('100%')
.padding({ left: 4, bottom: 8 })
Column({ space: 0 }) {
ForEach(items, (item, index) => {
this.buildSwitchItem(item.title, item.desc, item.key, item.value, index, items.length)
})
}
.backgroundColor('#FFFFFF')
.borderRadius(12)
}
}
@Builder
buildSwitchItem(title: string, desc: string, key: string, isOn: boolean, index: number, total: number) {
Column() {
Row() {
Column({ space: 4 }) {
Text(title).fontSize(15).fontColor('#333333')
Text(desc).fontSize(12).fontColor('#999999')
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
Toggle({ type: ToggleType.Switch, isOn: isOn })
.selectedColor('#4A9B6D')
.onChange((value: boolean) => {
this.saveSetting(key, value);
switch (key) {
case 'pushEnabled': this.pushEnabled = value; break;
case 'soundEnabled': this.soundEnabled = value; break;
case 'vibrationEnabled': this.vibrationEnabled = value; break;
case 'dailyReminder': this.dailyReminder = value; break;
case 'holidayNotification': this.holidayNotification = value; break;
}
promptAction.showToast({ message: value ? '已开启' : '已关闭' });
})
}
.width('100%')
.height(64)
.padding({ left: 16, right: 16 })
if (index < total - 1) {
Divider().height(1).color('#F0F0F0').margin({ left: 16 })
}
}
}
}
页面架构说明
NotificationSettings 页面
├── 导航栏(返回按钮 + 标题)
└── Scroll 滚动区域
├── 推送通知组(buildSwitchGroup)
│ ├── 允许推送(Toggle Switch)
│ ├── 通知声音(Toggle Switch)
│ └── 震动反馈(Toggle Switch)
├── 提醒设置组(buildSwitchGroup)
│ ├── 每日提醒(Toggle Switch)
│ └── 节气提醒(Toggle Switch)
└── 底部说明卡片
步骤6: 在EntryAbility中初始化
typescript
// entryability/EntryAbility.ets
import { NotificationService } from '../services/NotificationService';
export default class EntryAbility extends UIAbility {
async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
// 初始化各服务...
const storageService = new StorageService(this.context);
// 初始化通知服务
const notifService = NotificationService.getInstance();
await notifService.init(this.context, storageService);
// 将服务实例存入 AppStorage,供全局使用
AppStorage.setOrCreate('notificationService', notifService);
}
}
步骤7: 业务场景调用示例
场景一:节气当天自动推送
typescript
// 在首页或节气检测逻辑中调用
import { NotificationService } from '../services/NotificationService';
/** 检测今天是否为节气,若是则发送通知 */
async function checkAndNotifyTodaySolarTerm() {
const todaySolarTerm = getTodaySolarTerm(); // 自定义函数:获取今天的节气
if (todaySolarTerm) {
const service = NotificationService.getInstance();
const success = await service.sendHolidayNotification(
todaySolarTerm.name,
formatDate(new Date()),
`${todaySolarTerm.description}`
);
if (success) {
console.info(`节气通知已发送: ${todaySolarTerm.name}`);
}
}
}
场景二:每日定时知识推送
typescript
// 使用后台任务或 Timer 触发
import { NotificationService } from '../services/NotificationService';
/** 每日知识推送任务 */
async function dailyKnowledgeTask() {
const service = NotificationService.getInstance();
const knowledge = getRandomDailyKnowledge(); // 获取随机知识条目
await service.sendDailyKnowledge(knowledge.title, knowledge.content);
}
场景三:应用更新通知
typescript
// 在 AppUpdateService 检测到新版本时
async function notifyAppUpdate(version: string) {
const service = NotificationService.getInstance();
await service.sendSystemNotice(
'发现新版本',
`节气通 v${version} 已发布,点击前往更新。`
);
}
架构总览
┌─────────────────────────────────────────────────────┐
│ 用户界面层 │
│ ┌──────────────────┐ ┌─────────────────────────┐ │
│ │ NotificationSet- │ │ 首页 / 详情页 / 设置页 │ │
│ │ tings.ets │ │ (业务场景调用) │ │
│ └────────┬─────────┘ └────────────┬────────────┘ │
├───────────┼────────────────────────┼───────────────┤
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ 服务层 │ │
│ │ ┌─────────────────┐ ┌──────────────────┐ │ │
│ │ │ NotificationS- │ │ StorageService │ │ │
│ │ │ ervice (单例) │◄─┤ (设置持久化) │ │ │
│ │ └────────┬────────┘ └──────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────┐ │ │
│ │ │ @ohos.notificationManager (系统API) │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
├───────────────────────────────────────────────────┤
│ 系统层 │
│ ┌────────────────┐ ┌──────────────────────────┐ │
│ │ module.json5 │ │ 系统 Settings (通知权限) │ │
│ │ (权限声明) │ │ 通知中心 UI │ │
│ └────────────────┘ └──────────────────────────┘ │
└─────────────────────────────────────────────────────┘
数据流:
用户切换开关 → saveSetting() → Preferences 存储
│
业务触发通知 ← sendNotification() ← NotificationService
│
├─ 读取 pushEnabled (全局开关)
├─ 读取 holidayNotification (类型开关)
├─ 构建 NotificationRequest
└─ notificationManager.publish()
│
▼
系统通知中心展示
关键注意事项
1. 权限声明不可遗漏
module.json5 中必须声明 ohos.permission.NOTIFICATION_CONTROLLER,否则 publish() 调用会抛出权限异常。
2. 单例初始化时机
NotificationService.getInstance().init() 必须在使用任何发送方法之前调用。推荐在 EntryAbility.onCreate() 中统一初始化。
3. 设置变更实时生效
NotificationSettings 页面中的 Toggle 变更后立即写入 Preferences,下次发送通知时会重新读取最新值,无需重启应用。
4. 通知ID唯一性
同一ID的通知会被覆盖而非新增。本文采用「类型前缀 + 时间戳」策略确保每次发送都是新通知。
5. 异步方法全部使用 await
notificationManager.publish()、isNotificationEnabled()、Preferences 操作均为异步方法,必须使用 await 确保顺序执行。
最佳实践清单
在实现通知推送功能时,请逐项检查:
-
module.json5中已声明ohos.permission.NOTIFICATION_CONTROLLER -
NotificationService采用单例模式,全局唯一实例 -
init()方法在EntryAbility.onCreate()中调用 - 发送通知前检查
isInitialized状态 - 发送通知前读取用户设置(全局开关 + 类型开关)
- 通知ID基于类型+时间戳生成,避免冲突
- 所有异步操作均有 try-catch 错误处理
- 设置页面使用
Toggle组件,变更即时保存 - 设置页面支持 i18n 动态文本(
loadStrings()+ 监听器) -
extraInfo字段携带足够信息用于后续点击跳转 - 通知内容长度合理(正文建议不超过50字符)