1. 为什么要在OpenHarmony上做React Native本地通知先说说我为什么会趟这条河。团队去年接到一个适配任务把现有的React Native跨端应用迁移到OpenHarmony生态。业务倒不算复杂常规的列表、详情、表单真正卡住我的是推送通知模块。服务端推送要接厂商通道工期长、联调成本高PM当时拍板说先用本地通知顶一顶至少把提醒类场景跑起来。于是我开始研究React Native环境下PushNotification的本地通知到底怎么在OpenHarmony上落地。这里先厘清一个概念PushNotification这个库通常指react-native-push-notification它本身是一套本地通知封装也支持对接远程推送。在OpenHarmony上我们没有GCM、没有APNs甚至没有华为HMSCore那套PushKit的完整RN桥接至少在当时的版本下没有现成的。所以最务实的第一步就是先把本地通知打通——也就是不依赖网络、不依赖服务端纯客户端定时触发的提醒。比如用药提醒、待办事项、闹钟类应用这些都是典型场景。如果你也是RN开发者准备把应用跑到OpenHarmony设备上这篇文章大概率能帮你少踩几个坑。我会从环境准备、依赖选型、核心API的调用逻辑、以及我实际调试中遇到的诡异问题一条条讲清楚。全程基于我在真机DAYU200开发板OpenHarmony 3.2 Release上的实际操作不是纸上谈兵。2. OpenHarmony原生通知能力与RN桥接的现状2.1 先搞懂OpenHarmony自己的通知机制OpenHarmony从API 8开始就有一套完整的通知服务ohos.notificationManager这个模块负责创建通知渠道、发布通知、管理通知权限。它的核心流程和Android很像先要有个通知渠道Channel然后构造一个通知请求NotificationRequest最后调用publish发布出去。但RN应用跑在ArkUI的JS运行时里JS层是碰不到这些原生API的。这就需要一个桥要么自己写Native Module用ArkTS或C实现要么找现成的三方库。react-native-push-notification在Android和iOS上都有成熟的桥接实现但在OpenHarmony上官方仓库里根本没有对应的代码。所以网上搜OpenHarmony React Native PushNotification大概率只能搜到一堆不支持的帖子或者让你去改OpenHarmony源码——那太硬核了不适合普通业务团队。2.2 我最终选型自封装一个极简本地通知桥既然现成的轮子没有我的方案是自己封装一个轻量级NotificationBridge。注意这里说的封装不需要你去改React Native框架源码只需要用OpenHarmony的RN扩展能力写一个TurboModule或者普通的NativeModule然后在RN JS层调用。具体技术栈是这样OpenHarmony 3.2 Release支持React Native 0.72RNOHReact Native OpenHarmonyDevEco Studio 4.0API 9原生侧用ArkTS实现NotificationManager的封装JS侧用NativeModules调用为什么选ArkTS而不是C因为NotificationManager本身是JS/ArkTS接口直接用ArkTS封装最简单不需要跨语言的复杂转换。只有当你需要极高性能或者访问底层C接口时才值得考虑C。2.3 原生侧代码的最小实现在OpenHarmony的RN工程里找entry/src/main/ets/目录新建一个NotificationModule.ets。核心逻辑如下// NotificationModule.ets import notificationManager from ohos.notificationManager import { TurboModule } from rnoh/react-native-openharmony export class NotificationModule extends TurboModule { async createChannel(channelId: string, channelName: string) { const channel { id: channelId, name: channelName, importance: notificationManager.Importance.HIGH, slotType: notificationManager.SlotType.SOCIAL_COMMUNICATION } await notificationManager.addSlot(channel) return true } async publishLocalNotification(options: { channelId: string, title: string, body: string, triggerTime: number, id: number }) { let request { id: options.id, content: { title: options.title, text: options.body }, notificationSlotType: notificationManager.SlotType.SOCIAL_COMMUNICATION, deliveryTime: options.triggerTime, trigger: { // 定时触发 time: options.triggerTime } } await notificationManager.publish(request) return true } }这里有个关键点notificationManager.publish本身是即时发布的如果你要定时OpenHarmony支持deliveryTime加trigger的组合。但实测下来trigger.time只支持一次性定时不支持重复周期。如果你要做每天提醒这种场景原生侧做不到需要JS层自己算下一次触发时间然后重新调用publish。2.4 在OpenHarmony里注册这个模块模块写完后要在entry/src/main/ets/entryability/EntryAbility.ets里做包注册// EntryAbility.ets import { RNOHContext } from rnoh/react-native-openharmony const notificationModule new NotificationModule() // 在ReactNativeHost配置中注册 this.reactNativeHost.addTurboModule(notificationModule)具体注册方式取决于你的RNOH版本但大体思路一致。如果找不到addTurboModule方法也可以在getTurboModule的重写里按名字返回实例。3. RN JS层调用本地通知API设计与使用逻辑3.1 为什么不用react-native-push-notification原库在RN JS层本来可以直接import PushNotification from react-native-push-notification但这个库在OpenHarmony上没有原生实现import进来的只会是一个空壳。所以我选择自己写一个轻量wrapper只暴露业务需要的两个方法createChannel和scheduleNotification。这样做的好处有三个不依赖三方库的维护状态自己可控只暴露最小API避免一堆用不上的方法干扰团队后续如果接远程推送可以平滑扩展3.2 JS侧调用代码// NotificationService.ts import { NativeModules, Platform } from react-native const { NotificationModule } NativeModules export const NotificationService { async init() { // 在OpenHarmony上初始化默认通知渠道 if (Platform.OS harmony) { await NotificationModule?.createChannel(default_channel, 默认通知) } }, async scheduleLocalNotification({ id, title, body, triggerTime, channelId default_channel }) { if (Platform.OS ! harmony) { // 其他平台走原有PushNotification逻辑这里省略 return } await NotificationModule?.publishLocalNotification({ id, title, body, triggerTime, channelId }) } }这里有一个平台判断的细节Platform.OS在OpenHarmony的RNOH环境中返回的是harmony不是android也不是ios。如果你在代码里写死了Platform.OS android的兼容逻辑在OpenHarmony上会走不到正确的分支。这也是很多RN应用迁移到OpenHarmony后功能静默失效的常见原因之一。3.3 调用时机直接在业务里用比如一个待办应用用户设置了一个提醒import { NotificationService } from ./NotificationService const remindTime new Date(2025-01-20T09:00:00).getTime() NotificationService.scheduleLocalNotification({ id: 1001, title: 开会提醒, body: 项目周会记得带上日报, triggerTime: remindTime })发布之后系统会在指定时间弹出通知。实测在DAYU200上表现正常声音、震动、横幅都工作。但有几个前提条件我在下一节详细说。4. 权限、通知渠道与定时触发的坑4.1 动态权限申请必须先问用户OpenHarmony的通知发布需要权限吗这里有个容易搞混的点如果你的应用只是发布本地通知不读别人的通知那不需要ohos.permission.NOTIFICATION_CONTROLLER。但用户可能手动关掉你应用的通知权限所以你需要检查是否允许。检查与申请权限的代码如下import abilityAccessCtrl from ohos.abilityAccessCtrl const atManager abilityAccessCtrl.createAtManager() const permission ohos.permission.PUBLISH_AGENT_PREVIEW // 这个权限不对别用等一下上面那个权限是发布实况窗Live View用的普通本地通知不需要任何动态权限。真正的关键是通知中心里的应用通知开关必须是打开的。也就是说你需要引导用户到系统设置里把应用的通知开关打开。import notificationManager from ohos.notificationManager // 检查本应用通知是否开启 const isEnabled await notificationManager.isNotificationEnabled() if (!isEnabled) { // 跳转设置页或提示用户手动开启 }如果isNotificationEnabled()返回false即使你调用了publish通知也不会显示而且不会报任何错。这个坑我调试了整整半天代码都执行了日志也打了就是不见通知弹出来。查了半天发现是测试机的通知权限被关掉了。所以你在真机测试时第一件事就去设置里确认通知开关。4.2 通知渠道的优先级OpenHarmony的通知渠道Slot有几种类型SOCIAL_COMMUNICATION、SERVICE_INFORMATION、CONTENT_INFORMATION、OTHER_TYPES等。不同渠道类型影响通知的展示优先级和是否允许震动。如果你的通知是提醒类、闹钟类建议用SOCIAL_COMMUNICATION它的打扰级别较高会伴随声音和震动提醒。如果只是一般的资讯推送用CONTENT_INFORMATION更合适不容易过度打扰用户。注意同渠道ID不能重复创建你重复addSlot同一个ID会抛错或者静默失败。我建议在init时先调用getSlot查一下不存在再创建或者直接catch掉异常。4.3 定时触发的精度与限制OpenHarmony的trigger.time定时通知实测有约1~2秒的误差。原因不难理解通知服务本身不是硬实时系统加上系统可能有后台省电策略定时任务会被一定程度的延迟。如果你需要秒级精确的倒计时提醒本地通知方案就不太合适了改成应用内倒计时到点后发布立即通知会更靠谱。另一个限制系统对单个应用的通知数量有上限一般不超过几十条。如果你的应用频繁创建通知老的会被系统自动清理。所以对于待办提醒这类场景最好维护一个任务表更新时用同一ID覆盖旧通知。4.4 应用后台与通知的关系这里要特别说明OpenHarmony本地通知发布后即使应用被杀死通知依然会显示。因为通知一旦发布就由系统通知服务接管了。但如果你是在应用进程被杀之前用JS定时器setTimeout去触发通知那进程没了定时器也没了。正确做法是如果是未来某个时间的提醒应该把时间和通知内容一次性传入原生侧由原生侧交给系统定时。我上面的publishLocalNotification方法已经做了这一步triggerTime传入未来的毫秒时间戳即可。5. 我踩过的三个诡异Bug及完整排查链路5.1 Bug 1通知发布成功但屏幕不亮现象代码执行无异常但通知发出时屏幕没有任何反应只有下拉通知栏能看到。排查链路先检查通知权限isNotificationEnabled()返回true权限没问题再看通知渠道SOCIAL_COMMUNICATION应该会有横幅提醒最后发现是测试机的免打扰模式被触发了DAYU200平板的通知管理设置里有一个横幅通知显示的独立开关被前一个测试用例关掉了解决方案在设置里打开横幅通知并在设置request时显式加上content.banner相关的配置。这个不算代码问题但排查起来浪费了很多时间。提醒大家调通知类功能时先把系统通知设置里的每一项开关都摸清楚最好用一张截图记录初始状态。5.2 Bug 2重复创建Channel导致崩溃现象应用第二次进入页面时初始化调addSlot方法直接崩溃。排查链路第一次进入时channel创建成功第二次进入时再次调用addSlot底层报slot already exists的C错误我一开始以为是回调时机问题后来看了OpenHarmony的源码实现才发现addSlot对已存在的ID会尝试更新但部分旧版本会由于参数不一致触发内部错误解决方案在创建channel前先调用getSlotconst slot await notificationManager.getSlot(channelId) if (!slot) { await notificationManager.addSlot(channel) }这个防御性写法在OpenHarmony API 9和API 10上都验证过能彻底规避重复创建问题。同时也建议在初始化方法里包一层try-catch即使channel创建失败也不影响后续功能的降级。5.3 Bug 3deliveryTime与trigger.time同时设置导致行为异常现象我最初参考API文档同时设置了deliveryTime和trigger.time结果通知当场就弹出来了没有等到指定时间。排查链路API文档里确实有这两个字段但实测在某些版本下deliveryTime的优先级会覆盖trigger.time甚至可能导致逻辑冲突我把deliveryTime去掉只保留trigger.time就正常了解决方案定时触发场景下只设置trigger.time不要额外设置deliveryTime。deliveryTime更多用于展示通知的发送时间戳而不是真正的定时调度参数。这个细节文档里写得很含糊只有实操才能发现。6. 进阶给本地通知加上点击跳转和重复提醒6.1 点击通知跳转到指定页面本地通知不只是弹个横幅用户点击后往往要跳转到应用内对应页面。OpenHarmony通知支持wantAgent可以把点击事件绑定到一个Ability。在ArkTS侧我需要引入ohos.wantAgent模块import wantAgent from ohos.wantAgent const wantAgentInfo { wants: [ { bundleName: com.example.myapp, abilityName: EntryAbility, parameters: { route: task_detail, taskId: options.id } } ], operationType: wantAgent.OperationType.START_ABILITY, requestCode: options.id } const agent await wantAgent.getWantAgent(wantAgentInfo) request.wantAgent agent然后在EntryAbility的onNewWant或者onCreate里读取want.parameters.route通过Deep Link或者事件总线让RN页面响应跳转。我这里用的是RNOH自带的Linking模块从原生侧把URL传过去。具体做法是构造一个mychat://task_detail?id1001的URLRN侧用Linking.addEventListener(url, handler)监听。6.2 实现每天重复提醒前面说过trigger.time只支持一次性那每天8点提醒吃药怎么做两种方案方案A原生侧重复触发查看OpenHarmony API 10有没有repeat参数。目前API 10的定时通知支持按天、按周的重复。如果你用的是API 10以上版本在trigger里加repeatType即可trigger: { time: nextTime, repeatType: notificationManager.NotificationTriggerRepeatType.DAY }方案BJS侧轮询如果原生版本不支持那就老老实实在JS层算下一次触发时间每次通知发布成功后再排程下一天。这个方式的缺点是如果应用进程被杀后续提醒就断了除非你应用常驻后台。实测下来方案A是唯一可靠的。所以如果你要长期运行、反复提醒强烈建议升级到API 10以上的OpenHarmony版本至少是3.2 Release后的版本。6.3 本地通知的资源占用与性能最后提一个性能细节频繁创建通知对象、频繁调用publish对CPU和内存的占用其实很小。你不需要担心性能瓶颈。真正要关注的是电池优化策略如果用户开启了应用省电模式定时通知的触发时间可能会被推迟到下一个系统唤醒窗口。这其实是所有移动平台的共性不只是OpenHarmony。对用户来说差个几十秒通常可以接受。7. 实践总结这套方案的适用范围与未来扩展从我这次实际适配的经验来看在OpenHarmony上用RN做PushNotification本地通知完全走得通。核心工作集中在两块第一块是自研一个极简的原生通配桥调用系统NotificationManager第二块是RN侧做好平台分支确保在Platform.OS harmony时走对逻辑。如果你只是做提醒类功能这个方案已经够用。如果你的需求是远程推送那么本地通知这套可以保留作为兜底同时开始接厂商通道。我在实现里特意把publishLocalNotification和未来的pushRemoteNotification设计了同一个统一服务接口方便后面替换。最后分享一个实际操作中的体会OpenHarmony的文档更新速度很快很多API在不同版本间有小变化比如addSlot的行为、trigger.time的字段类型等。你在开发时一定要用当前设备系统版本对应的API文档为准不要拿网上旧版的示例直接照抄。我当时在API 9和API 10之间反复横跳被文档差异坑过好几回。建议先在模拟器上写一个最小demo验证API行为再动业务代码这是最稳妥的路径。