前言
本次参加开源鸿蒙跨平台开发学习活动,选择了 React Native 开发 HarmonyOS技术栈,在学习的同时顺便整理成一份系列笔记,记录从环境到开发的全过程。本篇作为第八篇,在前几篇文章中,我们已经完成了 React Native 项目在 OpenHarmony 上的环境搭建、基础组件使用、页面路由等内容。
从今天开始,我们进入"跨平台框架最核心的一环"------RN 与原生能力互相调用(双端通信)。
在 OpenHarmony(ArkTS/ArkUI)上集成 React Native 后,我们通常会遇到两类需求:
- JS → 原生(ArkTS/Harmony)调用
- 原生(ArkTS/Harmony) → JS 调用
这两部分共同构成 RN 新架构(TurboModules + Fabric)的核心通信桥。
我们将从实战角度说明在 OpenHarmony 中如何实现这两类调用,并结合具体代码示例来掌握"跨端通信"的完整方法。
一、RN 与 OpenHarmony 原生通信的整体架构
React Native 新架构(The New Architecture)基于以下能力构建:
-
TurboModule(JS 调用原生能力)
-
JSI(JavaScript Interface)高性能桥接层
-
Fabric UI Layer(跨端组件渲染)
在 OpenHarmony 上,RN 团队已经适配了 RNInstance、TurboModule、DeviceEventEmitter、CppComponentInstance 等桥接能力,使开发者可以:
-
在 JS 侧无缝调用 ArkTS 或 C++ 原生方法
-
在 ArkTS/C++ 侧向 JS 发送事件
-
组件可通过事件上报数据给 JS
-
原生模块可主动调用 JS Function
理解这些能力后,我们就可以开始实战啦。
二、JS → 原生:使用 TurboModule 调用 OpenHarmony 原生能力
在 RN 新架构中,JS 调用原生采用 TurboModule。
在 OpenHarmony 中也完全支持。
TurboModule 模块的实现流程:
-
定义原生模块接口(TypeScript 声明)
-
在 ArkTS 或 C++ 中实现对应方法
-
通过 TurboModule 注册
-
在 JS 侧调用
示例:
TypeScript
import { TurboModuleRegistry } from 'react-native';
export interface MyModuleSpec extends TurboModule {
getSystemInfo(): Promise<any>;
}
export default TurboModuleRegistry.getEnforcing<MyModuleSpec>('MyModule');
然后你就可以在 JS 中直接调用:
TypeScript
const info = await MyModule.getSystemInfo();
console.log(info);
如果你需要封装自己的模块,可以参考官方教程《自定义 TurboModule》。
三、原生 → JS:三种通信方式全面解析
OpenHarmony 原生侧(ArkTS / C++)可以通过多种方式向 RN JS 侧发送事件或调用回调:
方式一:DeviceEventEmitter(最常用的全局事件)
适用于:
-
原生向 JS 广播事件
-
如:监听硬件状态、传感器数据、UI 操作回调等
方式二:emitComponentEvent(组件事件机制)
适用于:
-
原生组件向 JS 同步 UI 事件
-
如:自定义组件的点击事件、加载事件、滑动事件等
方式三:callRNFunction(原生主动调用某个 JS 函数)
适用于:
-
原生内容加载完成时通知 JS
-
原生需要调用 JS 中某个模块的方法
-
C++ 层需要调用 JS 逻辑
下面详细讲解。
四、方式一:DeviceEventEmitter ------ 原生主动发送全局事件到 JS
ArkTS → JS 发送事件
你提供的示例中,ArkTS 使用 RNInstance.emitDeviceEvent:
TypeScript
this.ctx.rnInstance.emitDeviceEvent("clickMarqueeEvent", { params: { age: 18 } })
含义:
-
事件名:
clickMarqueeEvent -
携带数据:
{age:18}
JS 侧监听事件
TypeScript
import { DeviceEventEmitter } from 'react-native';
DeviceEventEmitter.addListener('clickMarqueeEvent', e => {
console.log('Receive native event:', e);
});
场景示例:
-
原生组件点击事件回传到 JS
-
原生文件下载进度通知 JS
-
原生网络变化通知 JS
五、方式二:emitComponentEvent ------ 原生 UI 组件事件上报
当你封装自定义原生组件(ArkTS / C++)时,需要将 UI 交互传回 JS。
ArkTS 调用
TypeScript
this.ctx.rnInstance.emitComponentEvent(tag, "onLoad", payload)
参数说明:
-
tag:组件节点 id(React Fiber 对应的原生视图)
-
eventName:事件名(如 onLoad、onClick)
-
payload:数据对象
JS 侧接收(组件 props 中)
TypeScript
<MyNativeView onLoad={(e) => console.log(e.nativeEvent)} />
C++ 示例
cpp
m_eventEmitter->dispatchEvent("load", [=](facebook::jsi::Runtime& runtime) {
auto payload = facebook::jsi::Object(runtime);
auto source = facebook::jsi::Object(runtime);
source.setProperty(runtime, "width", width);
source.setProperty(runtime, "height", height);
source.setProperty(runtime, "uri", uri.c_str());
return payload;
});
适用于:
-
图片加载事件
-
视频播放进度
-
自定义 UI 控件内部逻辑更新
-
任意"组件级"的事件回调
六、方式三:callRNFunction ------ 原生主动调用 JS 模块的方法
这个能力非常强大,它允许原生直接调用 JS 内的函数。
ArkTS 调用:
TypeScript
this.ctx.rnInstance.callRNFunction("JSTimers", "callTimers", [arg1, arg2]);
C++ 调用:
cpp
instance->callJSFunction("JSTimers", "callTimers", std::move(getObject(id)));
场景非常广:
-
原生某项任务完成 → 让 JS 执行逻辑
-
JS 中的某个模块需要由原生触发
-
C++ 层编写动画引擎、解码器,过程中主动调 JS
-
原生通知 JS 更新状态、刷新 UI
七、实战:实现一个 OpenHarmony 原生组件向 RN JS 发送点击事件
下面我们整合内容,给一个完整示例流程。
ArkTS 侧自定义组件中:
TypeScript
// 点击时通知 JS
onClick() {
this.ctx.rnInstance.emitDeviceEvent("onMarqueeClick", {
msg: "Marquee clicked",
timestamp: Date.now()
});
}
JS 侧监听:
TypeScript
useEffect(() => {
const listener = DeviceEventEmitter.addListener(
"onMarqueeClick",
(e) => {
console.log("来自原生的点击事件:", e);
}
);
return () => listener.remove();
}, []);
即可实现:
- ArkTS → JS 实时事件推送
- 支持自定义数据
- 生命周期安全、性能可靠
总结
OpenHarmony 上的 RN 原生通信能力非常完善,通过 TurboModules、DeviceEventEmitter、emitComponentEvent、callRNFunction,这些能力已经足够支撑:
-
全部系统能力调用(网络、传感器、相机、存储等)
-
UI 原生组件封装(ArkUI → JS)
-
原生主动推送事件
-
C++ 调用 JS,实现高性能模块(解码器、播放器)
在 HarmonyOS / OpenHarmony 的生态中,RN 依然可以保持高性能与灵活性,让 JS 与原生之间协作顺畅。