HarmonyOS 鸿蒙Next中本地通知与行程提醒功能实现
HarmonyOS 鸿蒙Next中本地通知与行程提醒功能实现 在开发旅行计划应用时,需要实现行程提醒功能:
- 用户设置行程后,在指定时间前(如提前30分钟)发送通知提醒
- 通知需要显示行程标题和开始时间
- 支持创建、更新、取消提醒
- 应用在后台时也能正常发送通知
1. 配置通知权限
在 module.json5 中无需额外配置权限,但需要在运行时请求通知权限。
2. 创建提醒服务(单例模式)
// services/ReminderService.ets
import { notificationManager } from '@kit.NotificationKit'
import { BusinessError } from '@kit.BasicServicesKit'
/**
* 提醒数据接口
*/
interface ReminderData {
itemId: string // 行程项ID(唯一标识)
title: string // 行程标题
scheduledDate: string // 行程日期 (YYYY-MM-DD)
startTime: string // 开始时间 (HH:mm)
minutesBefore: number // 提前多少分钟提醒
message?: string // 自定义提醒消息
}
/**
* 定时器信息接口
*/
interface TimerInfo {
timerId: number
reminderTime: number
}
/**
* 提醒服务类(单例模式)
*/
class ReminderServiceClass {
private static instance: ReminderServiceClass
private timerMappings: Map<string, TimerInfo> = new Map()
private isInitialized: boolean = false
private constructor() {}
public static getInstance(): ReminderServiceClass {
if (!ReminderServiceClass.instance) {
ReminderServiceClass.instance = new ReminderServiceClass()
}
return ReminderServiceClass.instance
}
}
3. 初始化并请求通知权限
/**
* 初始化服务
*/
public async initialize(): Promise<void> {
if (this.isInitialized) {
return
}
try {
// 请求通知权限
const hasPermission = await this.requestNotificationPermission()
if (!hasPermission) {
console.warn('[ReminderService] 没有通知权限')
}
this.isInitialized = true
console.info('[ReminderService] ✅ 初始化成功')
} catch (error) {
console.error('[ReminderService] 初始化失败:', error)
}
}
/**
* 请求通知权限
*/
private async requestNotificationPermission(): Promise<boolean> {
try {
// 检查通知是否已开启
const isEnabled = await notificationManager.isNotificationEnabled()
if (isEnabled) {
console.info('[ReminderService] 通知权限已开启')
return true
}
// 请求用户开启通知
await notificationManager.requestEnableNotification()
console.info('[ReminderService] 已请求开启通知')
return true
} catch (error) {
const err = error as BusinessError
if (err.code === 1600004) {
// 用户拒绝
console.warn('[ReminderService] 用户拒绝开启通知')
} else {
console.error('[ReminderService] 请求通知权限失败:', err.code, err.message)
}
return false
}
}
4. 创建行程提醒
/**
* 创建行程提醒
* @param data 提醒数据
* @returns 1=成功, -1=失败
*/
public async createReminder(data: ReminderData): Promise<number> {
if (!this.isInitialized) {
await this.initialize()
}
try {
// 计算提醒时间
const reminderTime = this.calculateReminderTime(
data.scheduledDate,
data.startTime,
data.minutesBefore
)
if (!reminderTime) {
console.warn('[ReminderService] 提醒时间计算失败')
return -1
}
const now = Date.now()
const delay = reminderTime.getTime() - now
// 检查提醒时间是否在未来
if (delay <= 0) {
console.warn('[ReminderService] 提醒时间已过,不创建提醒')
return -1
}
// 取消已有的同ID提醒
this.cancelReminder(data.itemId)
// 构建提醒内容
const content = data.message || `${data.minutesBefore}分钟后开始:${data.title}`
// 设置定时器
const timerId: number = setTimeout(() => {
this.sendNotification(data.itemId, '行程提醒', content)
this.timerMappings.delete(data.itemId)
}, delay) as number
// 保存定时器映射
this.timerMappings.set(data.itemId, {
timerId: timerId,
reminderTime: reminderTime.getTime()
})
console.info(`[ReminderService] ✅ 创建提醒成功`)
console.info(` 行程: ${data.title}`)
console.info(` 提醒时间: ${reminderTime.toLocaleString()}`)
console.info(` 延迟: ${Math.round(delay / 1000 / 60)}分钟后`)
return 1
} catch (error) {
console.error('[ReminderService] 创建提醒失败:', error)
return -1
}
}
/**
* 计算提醒时间
*/
private calculateReminderTime(
scheduledDate: string,
startTime: string,
minutesBefore: number
): Date | null {
try {
// 解析日期和时间
const dateParts = scheduledDate.split('-')
const timeParts = startTime.split(':')
if (dateParts.length < 3 || timeParts.length < 2) {
return null
}
const year = parseInt(dateParts[0])
const month = parseInt(dateParts[1]) - 1 // 月份从0开始
const day = parseInt(dateParts[2])
const hour = parseInt(timeParts[0])
const minute = parseInt(timeParts[1])
// 创建行程开始时间
const startDateTime = new Date(year, month, day, hour, minute, 0)
// 减去提前提醒的分钟数
const reminderTime = new Date(startDateTime.getTime() - minutesBefore * 60 * 1000)
return reminderTime
} catch (error) {
console.error('[ReminderService] 计算提醒时间失败:', error)
return null
}
}
5. 发送本地通知
/**
* 发送通知
*/
private async sendNotification(itemId: string, title: string, content: string): Promise<void> {
try {
// 生成唯一的通知ID
const notifyId = this.generateNotificationId(itemId)
// 创建通知请求
const notificationRequest: notificationManager.NotificationRequest = {
id: notifyId,
content: {
// 通知类型:基础文本
notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: title,
text: content
}
},
// 通知类型:社交通信(会显示在通知栏顶部)
notificationSlotType: notificationManager.SlotType.SOCIAL_COMMUNICATION
}
// 发布通知
await notificationManager.publish(notificationRequest)
console.info(`[ReminderService] ✅ 通知已发送: ${title} - ${content}`)
} catch (error) {
const err = error as BusinessError
console.error(`[ReminderService] 发送通知失败: ${err.code} - ${err.message}`)
}
}
/**
* 生成通知ID(基于字符串哈希)
*/
private generateNotificationId(itemId: string): number {
let hash = 0
for (let i = 0; i < itemId.length; i++) {
const char = itemId.charCodeAt(i)
hash = ((hash << 5) - hash) + char
hash = hash & hash // Convert to 32bit integer
}
return Math.abs(hash) % 2147483647 // 确保是正整数
}
6. 取消和更新提醒
/**
* 取消行程提醒
* @param itemId 行程项ID
*/
public async cancelReminder(itemId: string): Promise<boolean> {
try {
const timerInfo = this.timerMappings.get(itemId)
if (timerInfo) {
clearTimeout(timerInfo.timerId)
this.timerMappings.delete(itemId)
console.info(`[ReminderService] ✅ 取消提醒成功: ${itemId}`)
}
return true
} catch (error) {
console.error('[ReminderService] 取消提醒失败:', error)
return false
}
}
/**
* 更新行程提醒
*/
public async updateReminder(data: ReminderData): Promise<number> {
// 先取消旧提醒
await this.cancelReminder(data.itemId)
// 创建新提醒
return await this.createReminder(data)
}
/**
* 取消所有提醒
*/
public async cancelAllReminders(): Promise<void> {
this.timerMappings.forEach((timerInfo, itemId) => {
clearTimeout(timerInfo.timerId)
})
this.timerMappings.clear()
console.info('[ReminderService] ✅ 已取消所有提醒')
}
/**
* 发送测试通知
*/
public async sendTestNotification(): Promise<void> {
await this.sendNotification(
'test',
'测试提醒',
'这是一条测试通知,说明提醒功能正常工作!'
)
}
7. 导出单例
// 导出单例实例
export const reminderService = ReminderServiceClass.getInstance()
8. 在页面中使用
// pages/ScheduleDetailPage.ets
import { reminderService } from '../services/ReminderService'
@Entry
@Component
struct ScheduleDetailPage {
@State scheduleTitle: string = '参观故宫'
@State scheduleDate: string = '2024-12-25'
@State scheduleTime: string = '09:00'
@State reminderMinutes: number = 30
@State reminderSet: boolean = false
aboutToAppear(): void {
// 初始化提醒服务
reminderService.initialize()
}
build() {
Column({ space: 16 }) {
Text('行程详情')
.fontSize(24)
.fontWeight(FontWeight.Bold)
// 行程信息
Column({ space: 8 }) {
Text(`行程:${this.scheduleTitle}`)
Text(`日期:${this.scheduleDate}`)
Text(`时间:${this.scheduleTime}`)
}
.alignItems(HorizontalAlign.Start)
.width('100%')
// 提醒设置
Row() {
Text('提前提醒')
Select([
{ value: '15分钟' },
{ value: '30分钟' },
{ value: '1小时' },
{ value: '2小时' }
])
.selected(1)
.value('30分钟')
.onSelect((index: number) => {
const minutes = [15, 30, 60, 120]
this.reminderMinutes = minutes[index]
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
// 设置提醒按钮
Button(this.reminderSet ? '取消提醒' : '设置提醒')
.width('100%')
.height(48)
.backgroundColor(this.reminderSet ? '#FF4444' : '#A9846A')
.onClick(async () => {
if (this.reminderSet) {
// 取消提醒
await reminderService.cancelReminder('schedule_001')
this.reminderSet = false
} else {
// 创建提醒
const result = await reminderService.createReminder({
itemId: 'schedule_001',
title: this.scheduleTitle,
scheduledDate: this.scheduleDate,
startTime: this.scheduleTime,
minutesBefore: this.reminderMinutes
})
this.reminderSet = result === 1
}
})
// 测试按钮
Button('发送测试通知')
.width('100%')
.height(48)
.backgroundColor('#666666')
.onClick(() => {
reminderService.sendTestNotification()
})
}
.width('100%')
.padding(20)
}
}
效果
控制台日志:
[ReminderService] 通知权限已开启
[ReminderService] ✅ 初始化成功
[ReminderService] ✅ 创建提醒成功
行程: 参观故宫
提醒时间: 2024/12/25 08:30:00
延迟: 1440分钟后
[ReminderService] ✅ 通知已发送: 行程提醒 - 30分钟后开始:参观故宫
通知效果:
| 场景 | 表现 |
|---|---|
| 应用前台 | 状态栏显示通知,可下拉查看 |
| 应用后台 | 同上,通知正常发送 |
| 点击通知 | 可配置跳转到应用指定页面 |
通知类型说明
| SlotType | 说明 | 适用场景 |
|---|---|---|
SOCIAL_COMMUNICATION |
社交通信 | 即时消息、提醒 |
SERVICE_INFORMATION |
服务提醒 | 后台服务状态 |
CONTENT_INFORMATION |
内容资讯 | 新闻推送 |
OTHER_TYPES |
其他 | 通用通知 |
常见问题
Q1: 通知不显示?
排查步骤:
- 检查
isNotificationEnabled()返回值 - 确认用户已授权通知权限
- 检查通知ID是否冲突
Q2: 应用被杀后提醒失效?
原因: 定时器随应用进程销毁
解决方案: 使用系统级提醒代理(reminderAgentManager),本文方案适用于应用活跃期间的提醒
关键点总结
| 步骤 | 说明 |
|---|---|
| 1. 请求权限 | requestEnableNotification() |
| 2. 计算延迟 | 提醒时间 = 行程时间 - 提前分钟数 |
| 3. 设置定时器 | setTimeout() 延迟执行 |
| 4. 发送通知 | notificationManager.publish() |
| 5. 管理状态 | Map 保存定时器ID,支持取消 |
更多关于HarmonyOS 鸿蒙Next中本地通知与行程提醒功能实现的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
鸿蒙Next中本地通知使用NotificationManager发布,通过NotificationRequest设置通知内容、触发时间等属性。行程提醒功能基于ReminderRequest实现,可设置提醒时间、重复规则及提醒方式(如弹窗、震动)。两者均需在module.json5中声明相应权限,如ohos.permission.PUBLISH_AGENT_REMINDER。
在HarmonyOS Next中实现行程提醒功能,主要依赖后台任务管理和通知服务。以下是核心实现方案:
1. 后台任务与延时触发
使用@ohos.resourceschedule.backgroundTaskManager创建延时后台任务,确保应用退至后台或设备锁屏后仍能触发提醒。通过startBackgroundRunning()申请长时任务权限,结合delaySuspendManager.requestSuspendDelay()管理任务延迟执行。
2. 通知创建与发送
通过@ohos.notificationManager模块构建通知:
- 使用
NotificationRequest设置通知内容,在content中填入行程标题和开始时间。 - 通过
notification.publish()发送通知,支持在系统通知栏显示。
3. 提醒的增删改查
- 创建:计算触发时间(如行程开始前30分钟),调用后台任务接口注册延时任务,任务触发时发送通知。
- 更新:取消原有后台任务,重新创建新任务。
- 取消:直接取消对应的后台任务。
4. 关键配置
- 在
module.json5中声明ohos.permission.KEEP_BACKGROUND_RUNNING权限。 - 合理使用后台任务类型(如数据传输、音频播放等)以符合系统资源调度策略。
此方案能有效满足行程提醒的定时、后台触发和通知展示需求。

