HarmonyOs应用《重要日》开发第28篇 - 通知 ID 哈希算法与通知管理策略

发布时间:2026/7/21 17:10:05
HarmonyOs应用《重要日》开发第28篇 - 通知 ID 哈希算法与通知管理策略 本文深入解析 ImportantDays 项目中通知 ID 的生成算法、哈希冲突分析、通知的生命周期管理创建/覆盖/取消以及如何通过稳定的 ID 实现可靠的通知管理。一、通知 ID 的重要性在鸿蒙通知系统中每个通知需要一个唯一的数字 ID。通知 ID 用于标识通知系统通过 ID 区分不同通知覆盖更新相同 ID 的通知会覆盖旧通知精确取消通过 ID 取消特定通知如果 ID 管理不当可能导致通知重复不同通知用了相同 ID无法取消找不到通知 ID覆盖错误新通知覆盖了不该覆盖的二、ID 生成算法privatestaticnotificationIdBase10000;privategetNotificationId(dayID:string):number{lethash0;for(leti0;idayID.length;i){hash((hash5)-hash)dayID.charCodeAt(i);hash|0;}returnMath.abs(hash)%100000ImportantDayManager.notificationIdBase;}算法解析这是经典的djb2 哈希算法变体hashhash*31charCode等价于(hash 5) - hashhash 5hash * 32(hash 5) - hashhash * 31逐步执行示例以dayID 1721234567890_5678为例初始: hash 0 字符 1 (charCode49): hash (0 5) - 0 49 49 字符 7 (charCode55): hash (49 5) - 49 55 1568 - 49 55 1574 字符 2 (charCode50): hash (1574 5) - 1574 50 50368 - 1574 50 48844 ...继续处理每个字符 最终 hash 某个大整数可能溢出为负数 hash | 0 → 转为 32 位有符号整数 Math.abs(hash) % 100000 → 取 0-99999 范围 10000 → 最终范围 10000-109999关键操作说明操作作用hash 5左移5位 乘以32- hash减去自身 乘以31 charCode加上字符编码hash 0Math.abs(hash)取绝对值处理负数% 100000限制范围到 0-99999 10000偏移到 10000-109999为什么用hash | 0JavaScript/ArkTS 的数字是 64 位浮点数位运算会转为 32 位整数。hash | 0确保hash始终是 32 位有符号整数溢出时自动回绕不会变成浮点数负数被正确处理| 0保留符号位为什么加基数 10000returnMath.abs(hash)%10000010000;加基数 10000 确保通知 ID 不会是 0某些系统可能特殊处理 ID 0通知 ID 在一个明确的范围内10000-109999与其他可能的通知 ID 不冲突三、ID 稳定性分析同一 dayID → 相同 IDgetNotificationId(1721234567890_5678)// 总是返回相同的数字如 45678哈希函数是确定性的——相同输入总是产生相同输出。不同 dayID → 大概率不同 IDgetNotificationId(1721234567890_5678)// → 45678getNotificationId(1721234567890_5679)// → 23456不同getNotificationId(1721234567890_6789)// → 78901不同由于哈希函数的雪崩效应输入的微小变化会导致输出的巨大变化。四、哈希冲突分析冲突概率ID 范围10000-109999共 100000 个槽位。重要日数量冲突概率近似10~0.0045%50~1.2%100~4.7%500~71.8%对于个人使用场景通常几十条冲突概率极低。冲突的影响如果两个重要日的dayID哈希到同一个通知 ID重要日 A → notificationId 45678 重要日 B → notificationId 45678冲突 发送 A 的通知 → 通知栏显示 A 发送 B 的通知 → 通知栏显示 B覆盖了 AA 的通知被 B 覆盖用户只看到 B 的通知。冲突解决方向增大取模范围% 1000000而非% 100000使用计数器维护一个递增的 ID 分配器使用 dayID 的数字部分如果 dayID 包含时间戳可直接使用五、通知生命周期管理创建通知// 添加重要日时asyncaddDay(day:ImportantDay):Promisevoid{this.allDays.push(day);// ...if(day.reminderEnabled){this.scheduleReminder(day);// ← 创建通知}}privateasyncscheduleReminder(day:ImportantDay):Promisevoid{constdiffday.getDaysDiff();if(diff0)return;if(diffday.reminderDaysBefore){awaitNotificationUtil.publishBasic(即将到来${day.title},day.getDisplayText(),this.getNotificationId(day.dayID)// ← 使用稳定 ID);}}覆盖通知// 批量检查时checkAndSendRemindersfor(constdayofneedReminder){awaitNotificationUtil.publishBasic(title,content,this.getNotificationId(day.dayID)// ← 相同 ID 覆盖旧通知);}每次回前台时重新发送通知相同 ID 自动覆盖旧通知确保内容是最新的。取消通知// 删除重要日时asyncdeleteDay(dayID:string):Promisevoid{constdaythis.allDays[index];if(day.reminderEnabled){awaitNotificationUtil.cancel(this.getNotificationId(dayID));// ← 取消通知}this.allDays.splice(index,1);// ...}// 更新重要日时asyncupdateDay(day:ImportantDay):Promisevoid{awaitNotificationUtil.cancel(this.getNotificationId(day.dayID));// ← 先取消旧通知this.allDays[index]day;// ...if(day.reminderEnabled){this.scheduleReminder(day);// ← 再发新通知}}取消所有通知// 用户在我的页面操作NotificationUtil.cancelAll();生命周期图添加重要日启用提醒 ↓ scheduleReminder → publishBasic(id哈希值) ↓ 通知显示在通知栏 ↓ ├── 用户更新重要日 → cancel(id) → publishBasic(id) [新内容] ├── 用户删除重要日 → cancel(id) [通知消失] ├── 应用回前台 → publishBasic(id) [覆盖更新] └── 用户清除通知 → cancelAll() [全部消失]六、ID 与 dayID 的关系dayID 的生成staticgenerateId():string{return${Date.now()}_${Math.floor(Math.random()*10000)};}格式1721234567890_5678dayID → notificationId 的映射dayID (字符串) notificationId (数字) 1721234567890_5678 → 45678 1721234567890_1234 → 23456 1721234567891_8765 → 78901为什么不直接用 dayID 作为通知 ID通知 ID 必须是数字鸿蒙通知系统只接受number类型dayID 是字符串格式为时间戳_随机数需要确定性映射同一 dayID 每次都要得到相同的数字为什么不用时间戳部分// 不采用的方式constnotificationIdparseInt(dayID.split(_)[0]);时间戳值太大如 1721234567890可能超出通知 ID 的有效范围。哈希取模确保 ID 在合理范围内。七、通知管理的完整代码流EntryAbility.onCreate() ↓ Manager.init() ↓ loadImportantDays() → 加载数据 ↓ rebuildDayMap() → 构建索引 ↓ checkAndSendReminders() ↓ getDaysNeedReminder() → 筛选需要提醒的 ↓ checkPermission() → 检查权限 ↓ for each day: ↓ getNotificationId(dayID) → 哈希生成 ID ↓ publishBasic(title, content, id) → 发布通知 用户添加重要日启用提醒 ↓ Manager.addDay(day) ↓ allDays.push(day) ↓ rebuildDayMap() ↓ persist() → 持久化 ↓ scheduleReminder(day) ↓ getNotificationId(dayID) → 哈希生成 ID ↓ publishBasic(title, content, id) → 发布通知 用户更新重要日 ↓ Manager.updateDay(day) ↓ cancel(getNotificationId(dayID)) → 取消旧通知 ↓ allDays[index] day ↓ scheduleReminder(day) → 发布新通知 用户删除重要日 ↓ Manager.deleteDay(dayID) ↓ cancel(getNotificationId(dayID)) → 取消通知 ↓ allDays.splice(index, 1) ↓ rebuildDayMap() ↓ persist() EntryAbility.onForeground() ↓ Manager.checkAndSendReminders() → 重新检查并覆盖通知八、总结通知 ID 哈希算法和管理策略体现了以下设计确定性映射dayID → notificationId 的稳定哈希djb2 算法经典的字符串哈希分布均匀范围控制10000-109999避免 ID 过大或为 0覆盖策略相同 ID 自动覆盖避免重复通知完整生命周期创建 → 覆盖 → 取消每个阶段都有对应操作冲突可接受对于个人使用场景冲突概率极低通知 ID 虽然只是一个数字但它的生成和管理策略直接影响通知系统的可靠性。