鸿蒙原生开发手记:徒步迹 - 本地通知与提醒

鸿蒙原生开发手记:徒步迹 - 本地通知与提醒 鸿蒙原生开发手记徒步迹 - 本地通知与提醒使用 Notification Kit 实现本地通知与定时提醒前言本地通知不依赖远程服务器由应用自主触发适用于计时提醒、活动前提醒、轨迹记录状态提示等场景。Notification Kit 提供了丰富的通知样式和调度能力。一、通知渠道与权限import { notificationManager } from kit.NotificationServiceKit; import { BusinessError } from kit.BasicServicesKit; // 通知渠道类型 enum NotificationChannel { TRACKING tracking, // 轨迹记录 REMINDER reminder, // 提醒 ACTIVITY activity, // 活动 PROGRESS progress, // 进度 GENERAL general, // 通用 } // 渠道配置 const CHANNEL_CONFIG: RecordNotificationChannel, { name: string; description: string; slotType: notificationManager.SlotType; importance: notificationManager.Importance; enableSound?: boolean; enableVibrate?: boolean; } { [NotificationChannel.TRACKING]: { name: 轨迹记录, description: 轨迹记录状态通知, slotType: notificationManager.SlotType.SERVICE_INFORMATION, importance: notificationManager.Importance.HIGH, enableSound: false, enableVibrate: false, }, [NotificationChannel.REMINDER]: { name: 提醒, description: 定时活动提醒, slotType: notificationManager.SlotType.EVENT_REMINDER, importance: notificationManager.Importance.HIGH, enableSound: true, enableVibrate: true, }, [NotificationChannel.ACTIVITY]: { name: 活动, description: 团队活动通知, slotType: notificationManager.SlotType.SOCIAL_COMMUNICATION, importance: notificationManager.Importance.HIGH, enableSound: true, enableVibrate: true, }, [NotificationChannel.PROGRESS]: { name: 进度, description: 数据同步/导出进度, slotType: notificationManager.SlotType.OTHER, importance: notificationManager.Importance.MEDIUM, enableSound: false, }, [NotificationChannel.GENERAL]: { name: 通用, description: 其他通知, slotType: notificationManager.SlotType.OTHER, importance: notificationManager.Importance.MEDIUM, }, };二、通知管理器class NotificationService { private static instance: NotificationService; private notificationId: number 1000; private initialized: boolean false; static getInstance(): NotificationService { if (!NotificationService.instance) { NotificationService.instance new NotificationService(); } return NotificationService.instance; } // 初始化通知渠道 async init(context: common.UIAbilityContext): Promisevoid { if (this.initialized) return; try { // 请求通知权限 const granted await notificationManager.requestEnableNotification(context); if (!granted) { console.warn(通知权限未开启); return; } // 创建通知渠道 for (const [channel, config] of Object.entries(CHANNEL_CONFIG)) { const slot: notificationManager.NotificationSlot { type: config.slotType, name: config.name, description: config.description, level: config.importance, enableSound: config.enableSound ?? true, enableVibration: config.enableVibrate ?? true, }; await notificationManager.addSlot(slot); } this.initialized true; console.log(通知服务初始化完成); } catch (e) { console.error(通知服务初始化失败, (e as BusinessError).message); } } // 发送基本通知 async sendNotification(params: { title: string; text: string; channel?: NotificationChannel; wantAgent?: WantAgent; flags?: { sound?: boolean; vibrate?: boolean; light?: boolean; }; group?: string; progress?: { current: number; max: number }; }): Promisenumber { const id this.notificationId; const request: notificationManager.NotificationRequest { id, slotType: CHANNEL_CONFIG[params.channel ?? NotificationChannel.GENERAL].slotType, content: { contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, normal: { title: params.title, text: params.text, }, }, notificationFlags: { soundEnabled: params.flags?.sound, vibrationEnabled: params.flags?.vibrate, }, groupName: params.group, wantAgent: params.wantAgent, }; // 进度条通知 if (params.progress) { request.content.contentType notificationManager.ContentType.NOTIFICATION_CONTENT_PROGRESS; request.content.progress { title: params.title, text: params.text, progressValue: params.progress.current, progressMaxValue: params.progress.max, }; } await notificationManager.publish(request); return id; } // 发送定时提醒 async scheduleReminder(params: { title: string; text: string; triggerTime: Date; repeatInterval?: daily | weekly | monthly; data?: Recordstring, string; }): Promisevoid { const now Date.now(); const triggerMs params.triggerTime.getTime(); const delay triggerMs - now; if (delay 0) { console.warn(提醒时间已过); return; } // 使用定时任务 setTimeout(async () { await this.sendNotification({ title: params.title, text: params.text, channel: NotificationChannel.REMINDER, wantAgent: { pkgName: com.hiking.trail, abilityName: EntryAbility, parameters: params.data, }, }); // 重复提醒 if (params.repeatInterval) { this.scheduleReminder(params); // 递归调度下一次 } }, delay); } // 更新通知 async updateNotification(id: number, params: { title?: string; text?: string; progress?: { current: number; max: number }; }): Promisevoid { await notificationManager.publish({ id, content: { contentType: params.progress ? notificationManager.ContentType.NOTIFICATION_CONTENT_PROGRESS : notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, normal: { title: params.title ?? , text: params.text ?? , }, progress: params.progress ? { title: params.title ?? , text: params.text ?? , progressValue: params.progress.current, progressMaxValue: params.progress.max, } : undefined, }, } as notificationManager.NotificationRequest); } // 取消通知 async cancelNotification(id: number): Promisevoid { await notificationManager.cancel(id); } // 取消所有通知 async cancelAll(): Promisevoid { await notificationManager.cancelAll(); } // 获取通知权限状态 async isNotificationEnabled(): Promiseboolean { return await notificationManager.isNotificationEnabled(); } }三、轨迹记录前台通知Component struct TrackingNotification { private notificationService: NotificationService NotificationService.getInstance(); private notificationId: number 0; // 开始轨迹记录时发送前台通知 async startTrackingNotification(): Promisevoid { const id await this.notificationService.sendNotification({ title: 正在记录轨迹, text: GPS 定位中..., channel: NotificationChannel.TRACKING, flags: { sound: false, vibrate: false }, }); this.notificationId id; // 启动前台 Service (持续通知) try { await notificationManager.startServiceExtensionAbility( com.hiking.trail.TrackingService ); } catch (e) { console.error(启动前台 Service 失败, e); } } // 更新轨迹通知实时距离/时间 updateTrackingInfo(info: { distance: number; duration: number; pace: string; }): void { if (!this.notificationId) return; this.notificationService.updateNotification(this.notificationId, { title: 徒步记录中..., text: 距离: ${info.distance.toFixed(1)}km | 时长: ${info.duration}min | 配速: ${info.pace}/km, }); } // 暂停轨迹记录 async pauseTrackingNotification(): Promisevoid { if (!this.notificationId) return; await this.notificationService.updateNotification(this.notificationId, { title: 轨迹记录已暂停, text: 点击继续记录, }); } // 停止轨迹记录并移除通知 async stopTrackingNotification(): Promisevoid { if (!this.notificationId) return; await this.notificationService.sendNotification({ title: 轨迹记录完成, text: 本次徒步已保存, channel: NotificationChannel.TRACKING, }); await this.notificationService.cancelNotification(this.notificationId); this.notificationId 0; try { notificationManager.stopServiceExtensionAbility( com.hiking.trail.TrackingService ); } catch (e) { console.error(停止前台 Service 失败, e); } } }四、活动提醒调度class ActivityReminderScheduler { private notificationService: NotificationService NotificationService.getInstance(); private reminders: Mapnumber, { timer: number; triggerTime: Date } new Map(); // 设置活动提醒 async setActivityReminder(activity: { id: number; title: string; startTime: Date; location?: string; }): Promisevoid { // 活动开始前 30 分钟提醒 const remindTime new Date(activity.startTime.getTime() - 30 * 60 * 1000); await this.notificationService.scheduleReminder({ title: ️ 活动即将开始: ${activity.title}, text: activity.location ? 地点: ${activity.location} : 请做好准备, triggerTime: remindTime, data: { activityId: activity.id.toString() }, }); console.log(已设置活动提醒: ${activity.title}); } // 批量设置活动提醒 async setBatchReminders(activities: Array{ id: number; title: string; startTime: Date; location?: string; }): Promisevoid { for (const activity of activities) { await this.setActivityReminder(activity); } } // 取消活动提醒 cancelReminder(activityId: number): void { const reminder this.reminders.get(activityId); if (reminder) { clearTimeout(reminder.timer); this.reminders.delete(activityId); console.log(已取消提醒: ${activityId}); } } } // 数据导出进度通知 async function exportDataWithNotification(): Promisevoid { const notificationService NotificationService.getInstance(); // 发送进度通知 const id await notificationService.sendNotification({ title: 正在导出数据, text: 准备中..., channel: NotificationChannel.PROGRESS, }); const totalSteps 5; for (let i 1; i totalSteps; i) { await new Promise(resolve setTimeout(resolve, 1000)); await notificationService.updateNotification(id, { title: 正在导出数据, text: 步骤 ${i}/${totalSteps}..., progress: { current: i, max: totalSteps }, }); } // 导出完成 await notificationService.cancelNotification(id); await notificationService.sendNotification({ title: 导出完成, text: 数据已导出到文件, channel: NotificationChannel.PROGRESS, }); }五、通知权限引导页Entry Component struct NotificationGuidePage { State permissionGranted: boolean false; private notificationService: NotificationService NotificationService.getInstance(); aboutToAppear(): void { this.checkPermission(); } async checkPermission(): Promisevoid { this.permissionGranted await this.notificationService.isNotificationEnabled(); } async requestPermission(): Promisevoid { await this.notificationService.init(getContext(this) as common.UIAbilityContext); this.permissionGranted await this.notificationService.isNotificationEnabled(); } build() { Column() { if (this.permissionGranted) { // 已授权 Column() { Text(✅).fontSize(64); Text(通知已开启).fontSize(20).fontWeight(FontWeight.Bold).margin({ top: 16 }); Text(您将收到活动提醒和轨迹记录通知).fontSize(14).fontColor(#666).margin({ top: 8 }); } .alignItems(HorizontalAlign.Center).margin({ top: 80 }); } else { // 未授权 Column() { Text().fontSize(64); Text(开启通知权限).fontSize(20).fontWeight(FontWeight.Bold).margin({ top: 16 }); Text(及时获取活动提醒、团队邀请等消息).fontSize(14).fontColor(#666).margin({ top: 8 }); Button(开启通知) .backgroundColor(#4CAF50).fontColor(Color.White).borderRadius(24).width(200) .margin({ top: 32 }).onClick(() this.requestPermission()); } .alignItems(HorizontalAlign.Center).margin({ top: 80 }); } } .width(100%).height(100%).backgroundColor(Color.White); } }通知类型使用场景SlotType声音/震动轨迹记录前台持续通知SERVICE_INFORMATION静音定时提醒活动前30分钟EVENT_REMINDER有进度通知数据同步/导出OTHER无活动通知团队活动SOCIAL_COMMUNICATION有六、总结Notification Kit 提供了灵活的本地通知能力从基本文本通知到进度条、定时提醒覆盖了徒步迹的各种通知场景。前台通知让轨迹记录在后台也能保持用户感知定时提醒确保用户不会错过团队活动。下一篇文章将使用 Share Kit 实现应用间分享功能。下一篇预告鸿蒙原生开发手记徒步迹 - 分享功能Share Kit总结本文围绕“徒步迹“应用的实际开发场景系统讲解了相关技术的实现要点。通过代码实战原理剖析的方式帮助开发者快速掌握 HarmonyOS NEXT 的核心开发能力。总结要点理解 HarmonyOS NEXT 应用架构与 Ability 生命周期掌握 ArkUI 声明式 UI 的状态管理与组件化开发熟悉常用 Kit 能力Map Kit、Location Kit、Camera Kit 等的接入方式学会性能优化、内存管理、并发编程等进阶技巧具备从 0 到 1 构建完整 HarmonyOS 应用工程的能力核心特性回顾声明式 UIArkUI 提供简洁高效的声明式开发范式状态管理State、Prop、Link、Provide、Consume 等装饰器跨组件通信通过 Provide/Consume 实现跨层级数据传递原生能力通过 Kit 接入系统能力地图、定位、相机等性能优化LazyForEach、虚拟列表、Skeleton 骨架屏等学习建议技术学习重在实践建议结合项目源码同步动手操作遇到问题多查阅HarmonyOS 官方文档。下一篇预告鸿蒙原生开发手记徒步迹 - 持续更新中如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.netHarmonyOS 官方文档https://developer.huawei.com/consumer/cn//OpenHarmony 开源项目https://www.openharmony.cn/ArkUI 组件参考https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-ui-development徒步迹项目源码GitHub - hiking-trail-harmonyosDevEco Studio 下载https://developer.huawei.com/consumer/cn/deveco-studio/ArkTS 语言指南https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-overview系列文章导航CSDN 博客 - 鸿蒙原生开发手记