鸿蒙ArkTS与React Native JS双向通信实战教程

本文专注讲解鸿蒙ArkTS与React Native JS之间的双向通信原理和实战,涵盖从JS侧到ArkTS侧再到C++胶水层的完整链路。


总体架构理解

  • JS侧(React Native业务代码) :开发者日常写的JS/TS文件。
  • C++胶水层 :鸿蒙适配的桥接层,实现消息和数据的传递,配合TurboModule/JSI等机制。
  • ArkTS侧 :鸿蒙原生能力、页面、组件等实现,负责最终的业务落地。

通信流程简图如下:

scss 复制代码
JS (RN) <==> C++ (胶水层) <==> ArkTS (原生)

一、JS(RN)向ArkTS通信全流程

1.1 JS侧调用TurboModule

(1)声明TurboModule接口

typescript 复制代码
// src/specs/v2/NativeCalculator.ts
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import {TurboModuleRegistry} from 'react-native';

export interface Spec extends TurboModule {
  add(a: number, b: number): Promise<number>;
}
export default TurboModuleRegistry.get<Spec>('RTNCalculator') as Spec | null;

(2)实际调用

javascript 复制代码
import { RTNCalculator } from 'rtn-calculator';

const result = await RTNCalculator.add(3, 7);
// result 应为 10,实际走到了原生

1.2 C++胶水代码实现TurboModule

(1)胶水头文件生成(示例)

cpp 复制代码
// generated/RTNCalculator.h
#pragma once
#include "RNOH/TurboModule.h"
namespace rnoh {
class RTNCalculator : public ArkTSTurboModule {
public:
  RTNCalculator(const Context ctx, const std::string name);
};
}

(2)实现方法与注册

cpp 复制代码
// generated/RTNCalculator.cpp
#include "RTNCalculator.h"
namespace rnoh {
RTNCalculator::RTNCalculator(const Context ctx, const std::string name): ArkTSTurboModule(ctx, name) {
  methodMap_ = {
    { "add", { 2, [](facebook::jsi::Runtime& rt, facebook::react::TurboModule& turboModule, const facebook::jsi::Value* args, size_t count) {
      // 直接转发到ArkTS
      return static_cast<ArkTSTurboModule&>(turboModule).callAsync(rt, "add", args, count);
    }}}
  };
}
}

说明: 这里等价于将参数转发到ArkTS侧,由ArkTS实现业务。

1.3 ArkTS侧实现能力

typescript 复制代码
// entry/src/main/ets/turbomodule/CalculatorModule.ts
import { TurboModule } from '@rnoh/react-native-openharmony/ts';
import { TM } from '@rnoh/react-native-openharmony/generated/ts';
export class CalculatorModule extends TurboModule implements TM.RTNCalculator.Spec {
  add(a: number, b: number): Promise<number> {
    return Promise.resolve(a + b);
  }
}

1.4 回传结果到JS侧

ArkTS实现Promise返回,C++胶水自动转成JS的Promise,JS拿到最终结果。


二、ArkTS向JS(RN)通信全流程

2.1 ArkTS侧向JS侧发事件(DeviceEventEmitter示例)

typescript 复制代码
// ArkTS侧
this.ctx.rnInstance.emitDeviceEvent("customEvent", { foo: 123 });

2.2 C++胶水代码转发事件

cpp 复制代码
// C++实现
// 对应 emitDeviceEvent 方法内部
void RNInstance::emitDeviceEvent(const std::string& eventName, const folly::dynamic& payload) {
  // 实际会通过 JSI 调用 JS 侧的 DeviceEventEmitter.emit
  // 伪代码示例:
  auto jsCallback = ...; // 得到JSEmit函数
  jsCallback(eventName, payload);
}

2.3 JS侧监听事件

javascript 复制代码
import { DeviceEventEmitter } from 'react-native';

DeviceEventEmitter.addListener('customEvent', (data) => {
  // data = { foo: 123 }
  // 这里处理ArkTS侧发来的消息
});

三、JS(RN)侧主动监听Native事件

同上,JS侧通过DeviceEventEmitter或TurboModule的回调接口监听原生主动推送过来的事件。


四、ArkTS与JS直接消息通信(消息总线机制)

ArkTS向C++侧发消息:

typescript 复制代码
// ArkTS
this.ctx.rnInstance.postMessageToCpp("SAMPLE_MESSAGE", { foo: "bar" });

C++胶水层Observer监听:

cpp 复制代码
class MyComponentInstance : public CppComponentInstance<...>, public ArkTSMessageHub::Observer {
public:
  MyComponentInstance(Context context)
    : CppComponentInstance(std::move(context)), ArkTSMessageHub::Observer(m_deps->arkTSMessageHub) {}
  void onMessageReceived(ArkTSMessage const& message) override {
    if (message.name == "SAMPLE_MESSAGE") {
      // 处理 message.payload
      // 可再通过 emitDeviceEvent 转发给 JS
    }
  }
};

C++向ArkTS侧发消息:

cpp 复制代码
m_deps->rnInstance.lock()->postMessageToArkTS("ANOTHER_MESSAGE", { key: "value" });

ArkTS侧监听C++消息:

typescript 复制代码
const unsubscribe = rnInstance.cppEventEmitter.subscribe("ANOTHER_MESSAGE", (value: object) => {
  // value = { key: "value" }
  unsubscribe();
});

五、核心要点总结

  • RN (JS) 与 ArkTS 之间通信,C++胶水层是桥梁,开发者主要关注两端API用法即可。
  • JS侧调用TurboModule方法 → C++胶水转发 → ArkTS实现能力 → Promise返回JS
  • ArkTS/Native侧发事件 → C++胶水转发 → DeviceEventEmitter/事件总线 → JS侧监听响应
  • 消息总线机制支持双向自定义事件/消息传递,适合复杂场景。

结语

只要理解"一切通信必经C++胶水层",并用好TurboModule/DeviceEventEmitter/消息总线,鸿蒙ArkTS与React Native JS的双端通信就会变得非常清晰、简单!

相关推荐
星栈6 小时前
LiveView 怎么测:从组件测试到端到端,我终于不再只敢手点页面了
前端·前端框架·elixir
光影少年8 小时前
RN 渲染原理:不依赖WebView,原生控件渲染
前端·react native·react.js·前端框架
LaughingZhu1 天前
智能体经典范式构建:ReAct、Plan-and-Solve 与 Reflection
前端·react.js·前端框架
binbin_521 天前
React 井字棋教程:用一个小游戏理解组件和状态
前端·react.js·前端框架
禅思院1 天前
AI对话前端从入门到崩溃:一个长对话引发的五层优化战争【三】
前端·架构·前端框架
YIAN2 天前
LangChain JS 工具调用实战:基于 DeepSeek-V4-Flash 实现本地文件读取 AI 代码助手(完整可运行源码)
javascript·langchain·前端框架
星栈2 天前
LiveView 的认证系统:从登录到权限,我一开始以为有 `current_user` 就算完事了
前端·前端框架·elixir
GISer_Jing2 天前
从 Demo 到 Production:拆解 Superpower、SDD、Harness 与 Loop 的现代 AI 落地范式
前端·人工智能·ai·前端框架
wanghowie2 天前
LangGraph4j 落地(二)— ReAct 多步工具与 trace 扩展
前端·react.js·前端框架
sun_weitao2 天前
微前端框架之qiankun 怎么用?vue3+vite+qiankun 和 react + qiankun
前端·react.js·前端框架