目录
[React Native 三方库使用与集成](#React Native 三方库使用与集成)
[1.1 识别适配包 (TPL)](#1.1 识别适配包 (TPL))
[1.2 安装核心依赖](#1.2 安装核心依赖)
[2.1 规范化开发:路由常量](#2.1 规范化开发:路由常量)
[2.2 嵌套导航实现逻辑](#2.2 嵌套导航实现逻辑)
[章节三:开启 C-API 高性能架构](#章节三:开启 C-API 高性能架构)
[3.1 核心配置步骤](#3.1 核心配置步骤)
[4.1 第一步:Native 依赖链接 (ohpm)](#4.1 第一步:Native 依赖链接 (ohpm))
[4.2 第二步:C++ 源码链接 (CMake)](#4.2 第二步:C++ 源码链接 (CMake))
[4.3 第三步:Codegen 自动生成胶水代码](#4.3 第三步:Codegen 自动生成胶水代码)
[4.4 第四步:原生包注册 (PackageProvider.cpp)](#4.4 第四步:原生包注册 (PackageProvider.cpp))
[4.5 第五步:ArkTS 工厂注册 (RNPackagesFactory.ets)](#4.5 第五步:ArkTS 工厂注册 (RNPackagesFactory.ets))
[5.1 ArkTS 组件白名单](#5.1 ArkTS 组件白名单)
[5.2 委托构建器](#5.2 委托构建器)
项目初始化
前置条件
软件环境 DevEco Studio、VS Code、Node.js
初始化
-
初始化ReactNayive项目
npx react-native@0.72.5 init RNNavigation --version 0.72.5
-
安装依赖
npm i @react-native-oh/react-native-harmony@0.72.90
-
修改 package.json 文件
{
"name": "RNNavigation",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"lint": "eslint .",
"start": "react-native start",
"test": "jest",
"harmony": "react-native bundle-harmony --dev" //需要添加的配置项
},
"dependencies": {
"@react-native-oh/react-native-harmony": "^0.72.90", //此项会自动写入
"react": "18.2.0",
"react-native": "0.72.5"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native/eslint-config": "^0.72.2",
"@react-native/metro-config": "^0.72.11",
"@tsconfig/react-native": "^3.0.0",
"@types/react": "^18.0.24",
"@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.2.1",
"eslint": "^8.19.0",
"jest": "^29.2.1",
"metro-react-native-babel-preset": "0.76.8",
"prettier": "^2.4.1",
"react-test-renderer": "18.2.0",
"typescript": "4.8.4"
},
"engines": {
"node": ">=16"
}
} -
创建harmony项目

-
安装rn harmony 依赖
ohpm i @rnoh/react-native-openharmony@0.72.90

-
配置 Metro 打包器以支持 Harmony
/**
- @ProjectName : RNNavigation
- @Author : GuoJiaHui
- @Time : 2026年04月19日 10:00 AM
- @Description : Metro bundler configuration
*/
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
const {
createHarmonyMetroConfig,
} = require('@react-native-oh/react-native-harmony/metro.config');
/**
- Metro configuration for OpenHarmony
- Integrates RNOH-specific resolver and transformer rules.
- @type {import('metro-config').MetroConfig}
*/
const config = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
};
module.exports = mergeConfig(
getDefaultConfig(__dirname),
createHarmonyMetroConfig({
reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony',
}),
config,
); -
初始化编译
npm run harmony

- 修改Haymony工程
这里需要新建两个文件

project(rnapp)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_SKIP_BUILD_RPATH TRUE)
set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../oh_modules")
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(RNOH_CPP_DIR "${OH_MODULE_DIR}/@rnoh/react-native-openharmony/src/main/cpp")
set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated")
set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
add_compile_definitions(WITH_HITRACE_SYSTRACE)
set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
add_library(rnoh_app SHARED
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp" "./PackageProvider.cpp")
target_link_libraries(rnoh_app PUBLIC rnoh)
//
// Created on 2026/1/29.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".
#include "RNOH/PackageProvider.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {};
}
修改这个文件 src/main/ets/entryability/EntryAbility.ets
import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { RNAbility } from '@rnoh/react-native-openharmony';
const DOMAIN = 0x0000;
export default class EntryAbility extends RNAbility {
protected getPagePath(): string {
return "pages/Index"
}
override onCreate(want: Want): void {
super.onCreate(want);
try {
this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
} catch (err) {
hilog.error(DOMAIN, 'testTag', 'Failed to set colorMode. Cause: %{public}s', JSON.stringify(err));
}
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
}
onDestroy(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});
}
onWindowStageDestroy(): void {
// Main window is destroyed, release UI related resources
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
// Ability has brought to foreground
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
}
}
新建文件 src/main/ets/RNPackagesFactory.ets
import { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [];
}
修改 harmony\entry\build-profile.json5
{
"apiType": "stageMode",
"buildOption": {
"externalNativeOptions":{
"path":"./src/main/cpp/CMakeLists.txt",
"arguments":"",
"cppFlags":"",
"abiFilters":["arm64-v8a","x86_64"]
},
"resOptions": {
"copyCodeResource": {
"enable": false
}
}
},
"buildOptionSet": [
{
"name": "release",
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": false,
"files": [
"./obfuscation-rules.txt"
]
}
}
}
},
],
"targets": [
{
"name": "default"
},
{
"name": "ohosTest",
}
]
}
修改 index.ets
import {
AnyJSBundleProvider,
ComponentBuilderContext,
FileJSBundleProvider,
MetroJSBundleProvider,
ResourceJSBundleProvider,
RNApp,
RNOHErrorDialog,
RNOHLogger,
TraceJSBundleProviderDecorator,
RNOHCoreContext
} from '@rnoh/react-native-openharmony';
import { createRNPackages } from '../RNPackagesFactory';
@Builder
export function buildCustomRNComponent(ctx: ComponentBuilderContext) {}
const wrappedCustomRNComponentBuilder = wrapBuilder(buildCustomRNComponent)
@Entry
@Component
struct Index {
@StorageLink('RNOHCoreContext') private rnohCoreContext: RNOHCoreContext | undefined = undefined
@State shouldShow: boolean = false
private logger!: RNOHLogger
aboutToAppear() {
this.logger = this.rnohCoreContext!.logger.clone("Index")
const stopTracing = this.logger.clone("aboutToAppear").startTracing();
this.shouldShow = true
stopTracing();
}
onBackPress(): boolean | undefined {
// NOTE: this is required since `Ability`'s `onBackPressed` function always
// terminates or puts the app in the background, but we want Ark to ignore it completely
// when handled by RN
this.rnohCoreContext!.dispatchBackPress()
return true
}
build() {
Column() {
if (this.rnohCoreContext && this.shouldShow) {
if (this.rnohCoreContext?.isDebugModeEnabled) {
RNOHErrorDialog({ ctx: this.rnohCoreContext })
}
RNApp({
rnInstanceConfig: {
createRNPackages,
enableNDKTextMeasuring: true, // 该项必须为true,用于开启NDK文本测算
enableBackgroundExecutor: false,
enableCAPIArchitecture: true, // 该项必须为true,用于开启CAPI
arkTsComponentNames: []
},
initialProps: { "foo": "bar" } as Record<string, string>,
appKey: "RNNavigation",
wrappedCustomRNComponentBuilder: wrappedCustomRNComponentBuilder,
onSetUp: (rnInstance) => {
rnInstance.enableFeatureFlag("ENABLE_RN_INSTANCE_CLEAN_UP")
},
jsBundleProvider: new TraceJSBundleProviderDecorator(
new AnyJSBundleProvider([
new MetroJSBundleProvider(),
// NOTE: to load the bundle from file, place it in
// `/data/app/el2/100/base/com.rnoh.tester/files/bundle.harmony.js`
// on your device. The path mismatch is due to app sandboxing on OpenHarmony
new FileJSBundleProvider('/data/storage/el2/base/files/bundle.harmony.js'),
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'hermes_bundle.hbc'),
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js')
]),
this.rnohCoreContext.logger),
})
}
}
.height('100%')
.width('100%')
}
}
- 运行项目



至此,完成了ReactNative for OpenHarmany的工程初始化
模板工程地址:RNNavigation
React Native 三方库使用与集成
通过 react-navigation 实战案例,完成从 三方库引入 、业务页面实现 到 C-API 高性能架构配置 及 原生模块底层桥接 的全链路开发。
章节一:三方库的选择与集成
在 HarmonyOS RN 开发中,第一步是确保你使用的三方库能够运行在鸿蒙系统上。
1.1 识别适配包 (TPL)
为什么需要适配版本?
在 HarmonyOS RN 中,带有原生代码(C++/ArkUI)的三方库需要经过专门适配才能运行。
- 命名规范 :通常带有
@react-native-oh-tpl前缀。 - 版本匹配 :确保适配包的版本与你项目中的
react-native-harmony版本兼容。
1.2 安装核心依赖
以导航库为例,安装以下核心组件:
# 安装基础导航容器
npm install @react-navigation/native@6.1.17
# 安装鸿蒙适配的原生组件 (TPL 版本)
# 提示:^ 符号表示兼容后续小版本更新
npm install @react-native-oh-tpl/react-native-gesture-handler@^2.14.16
npm install @react-native-oh-tpl/react-native-safe-area-context@^4.7.4-0.2.1
npm install @react-native-oh-tpl/react-native-screens@^3.34.0-0.0.1
npm install @react-native-oh-tpl/stack@6.4.0-0.0.4
章节二:业务页面与导航体系实现
2.1 规范化开发:路由常量
文件: src/constants/routes.ts 操作: 严禁使用硬编码字符串。定义全大写的路由常量,便于在原生侧注册白名单时参考。
/**
* 首页路由常量
* 对应 Tab 中的第一个页面
*/
export const ROUTE_HOME = 'HOME';
/**
* 详情页路由常量
* 用于展示点击列表后的具体内容,演示参数传递
*/
export const ROUTE_DETAILS = 'DETAILS';
/**
* 教学说明/关于页路由常量
* 对应 Tab 中的第二个页面
*/
export const ROUTE_INFO = 'INFO';
/**
* 演示列表页路由常量
* 用于展示不同的导航案例,演示堆栈跳转
*/
export const ROUTE_DEMO_LIST = 'DEMO_LIST';
/**
* 标签页导航器路由常量
* 这是一个"包装路由",它包裹了首页和关于页
*/
export const ROUTE_TAB_NAVIGATOR = 'TAB_NAVIGATOR';
2.2 嵌套导航实现逻辑
文件: RootNavigator.tsx 操作要点:
-
初始化 Stack:创建根导航器,负责管理全局页面(如详情页、登录页)。
-
初始化 Tabs:创建底部菜单导航器。
-
嵌套声明 :将
TabNavigator作为Stack.Screen的一个component传入。/**
-
@ProjectName : RNNavigation
-
@Author : GuoJiaHui
-
@Time : 2026年04月20日 00:25 AM
-
@Description : 根导航器配置。这里展示了 React Navigation 的灵魂:嵌套导航。
-
教学点:
-
- 堆栈导航 (Stack):像剥洋葱,一层包一层,适合从列表页跳到详情页。
-
- 标签导航 (Tab):像切频道,平级切换,适合底部菜单。
-
- 嵌套逻辑:Stack 里面可以装 Tab,Tab 里面也可以装 Stack。
*/
import React from 'react';
// 核心容器:负责管理整个导航状态
import {NavigationContainer} from '@react-navigation/native';
// 堆栈导航器:实现页面推入和弹出的效果
import {createStackNavigator} from '@react-native-oh-tpl/stack';
// 底部标签导航器:实现底部 Tab 切换效果
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
// 导入所有页面
import HomePage from '../pages/HomePage';
import DemoListPage from '../pages/DemoListPage';
import DetailsPage from '../pages/DetailsPage';
import InfoPage from '../pages/InfoPage';
// 导入路由常量:统一路由名称,防止手抖写错
import {
ROUTE_HOME,
ROUTE_DEMO_LIST,
ROUTE_DETAILS,
ROUTE_INFO,
ROUTE_TAB_NAVIGATOR,
} from '../constants/routes';
// 创建两个导航器工厂实例
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
/**
-
【内层导航】底部标签页导航器
-
负责应用底部的两个主入口:首页、关于页。
*/
const TabNavigator = () => {
console.log('[RootNavigator] 渲染底部 Tab 栏');
return (
<Tab.Navigator screenOptions={{ tabBarActiveTintColor: '#4F46E5', // 选中时的颜色(教学 Indigo 色) tabBarInactiveTintColor: '#94A3B8', // 未选中时的颜色 headerShown: false, // 隐藏 Tab 内部页面的默认顶部标题,因为我们通常在 Stack 里统一定义 tabBarStyle: { backgroundColor: '#FFFFFF', borderTopWidth: 0, elevation: 10, // 鸿蒙/安卓的阴影 height: 60, paddingBottom: 8, }, }}> <Tab.Screen name={ROUTE_HOME} component={HomePage} options={{tabBarLabel: '首页'}} /> <Tab.Screen name={ROUTE_INFO} component={InfoPage} options={{tabBarLabel: '关于'}} /> </Tab.Navigator>);
};
/**
-
【外层导航】根堆栈导航器
-
负责处理全局的页面跳转逻辑。
*/
export const RootNavigator = () => {
console.log('[RootNavigator] 初始化全局 Stack 导航');
return (
/** * NavigationContainer 是导航系统的根。 * 所有的导航器(Stack, Tab 等)必须被它包裹。 */ <NavigationContainer> <Stack.Navigator initialRouteName={ROUTE_TAB_NAVIGATOR} // 应用启动时首先显示哪个路由 screenOptions={{ // 全局统一的顶部标题栏样式 headerStyle: { backgroundColor: '#EEF2FF', elevation: 0, // 去掉阴影,让 UI 看起来更现代、平滑 }, headerTintColor: '#312E81', // 标题和返回按钮的颜色 headerTitleStyle: {fontWeight: 'bold'}, headerBackTitleVisible: false, // 隐藏 iOS 风格的返回文字,只留箭头 }}> {/* 1. 嵌套路由:第一个页面就是上面的 TabNavigator。 这意味着我们的底部菜单是应用的主框架。 */} <Stack.Screen name={ROUTE_TAB_NAVIGATOR} component={TabNavigator} options={{headerShown: false}} // 隐藏外层 Stack 的标题,使用内层或不显示 /> {/* 2. 独立页面:这些页面不在底部菜单里。 当我们在首页点击"开始学习"时,会通过 Stack 把 DemoListPage 推到屏幕前。 */} <Stack.Screen name={ROUTE_DEMO_LIST} component={DemoListPage} options={{title: '学习演示'}} /> {/* 3. 动态标题页面:演示如何根据参数动态设置标题。 */} <Stack.Screen name={ROUTE_DETAILS} component={DetailsPage} options={({route}: any) => ({ title: route.params?.title || '详情', // 如果传了 title 参数就用它,否则默认显示"详情" })} /> </Stack.Navigator> </NavigationContainer>);
};
-
章节三:开启 C-API 高性能架构
3.1 核心配置步骤
文件: Index.ets 操作细节:
-
启用标志位 :设置
enableCAPIArchitecture: true。 -
文本测算优化 :设置
enableNDKTextMeasuring: true(这是 C-API 架构下的推荐配置,能大幅提升文字渲染性能)。 -
设置 appKey :确保
appKey与 JS 侧AppRegistry.registerComponent的第一个参数完全一致。import {
AnyJSBundleProvider,
ComponentBuilderContext,
FileJSBundleProvider,
MetroJSBundleProvider,
ResourceJSBundleProvider,
RNApp,
RNOHErrorDialog,
RNOHLogger,
TraceJSBundleProviderDecorator,
RNOHCoreContext
} from '@rnoh/react-native-openharmony';
import { createRNPackages } from '../RNPackagesFactory';
import { SAFE_AREA_VIEW_TYPE, SafeAreaView, SAFE_AREA_PROVIDER_TYPE, SafeAreaProvider } from "@react-native-oh-tpl/react-native-safe-area-context";
import { componentBuilder as RNScreensComponentBuilder } from '@react-native-oh-tpl/react-native-screens';@Builder
export function buildCustomRNComponent(ctx: ComponentBuilderContext) {
if (ctx.componentName === SAFE_AREA_VIEW_TYPE) {
SafeAreaView({
ctx: ctx.rnComponentContext,
tag: ctx.tag,
buildCustomComponent: buildCustomRNComponent
})
} else if (ctx.componentName === SAFE_AREA_PROVIDER_TYPE) {
SafeAreaProvider({
ctx: ctx.rnComponentContext,
tag: ctx.tag,
buildCustomComponent: buildCustomRNComponent
})
} else {
RNScreensComponentBuilder(ctx)
}
}const wrappedCustomRNComponentBuilder = wrapBuilder(buildCustomRNComponent)
@Entry
@Component
struct Index {
@StorageLink('RNOHCoreContext') private rnohCoreContext: RNOHCoreContext | undefined = undefined
@State shouldShow: boolean = false
private logger!: RNOHLoggeraboutToAppear() {
this.logger = this.rnohCoreContext!.logger.clone("Index")
const stopTracing = this.logger.clone("aboutToAppear").startTracing();this.shouldShow = true stopTracing();}
onBackPress(): boolean | undefined {
// NOTE: this is required sinceAbility'sonBackPressedfunction always
// terminates or puts the app in the background, but we want Ark to ignore it completely // when handled by RN this.rnohCoreContext!.dispatchBackPress()
return true
}build() {
Column() {
if (this.rnohCoreContext && this.shouldShow) {
if (this.rnohCoreContext?.isDebugModeEnabled) {
RNOHErrorDialog({ ctx: this.rnohCoreContext })
}
RNApp({
rnInstanceConfig: {
createRNPackages,
enableNDKTextMeasuring: true, // 该项必须为true,用于开启NDK文本测算
enableBackgroundExecutor: false,
enableCAPIArchitecture: true, // 该项必须为true,用于开启CAPI
arkTsComponentNames: [
SAFE_AREA_VIEW_TYPE,
SAFE_AREA_PROVIDER_TYPE,
"RNSScreen",
"RNSScreenContainer",
"RNSScreenNavigationContainer",
"RNSScreenStack",
"RNSScreenStackHeaderConfig",
"RNSScreenStackHeaderSubview",
"RNSFullWindowOverlay",
"RNSModalScreen",
"RNSSearchBar"
]
},
initialProps: { "foo": "bar" } as Record<string, string>,
appKey: "rn-navigation",
wrappedCustomRNComponentBuilder: wrappedCustomRNComponentBuilder,
onSetUp: (rnInstance) => {
rnInstance.enableFeatureFlag("ENABLE_RN_INSTANCE_CLEAN_UP")
},
jsBundleProvider: new TraceJSBundleProviderDecorator(
new AnyJSBundleProvider([
new MetroJSBundleProvider(),
// NOTE: to load the bundle from file, place it in
///data/app/el2/100/base/com.rnoh.tester/files/bundle.harmony.js// on your device. The path mismatch is due to app sandboxing on OpenHarmony new FileJSBundleProvider('/data/storage/el2/base/files/bundle.harmony.js'),
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'hermes_bundle.hbc'),
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js')
]),
this.rnohCoreContext.logger),
})
}
} .height('100%')
.width('100%')
}
}
章节四:原生模块底层桥接
这是最核心的步骤,决定了三方库的原生功能(如手势、屏幕管理)能否被 JS 调用。
4.1 第一步:Native 依赖链接 (ohpm)
在 harmony/oh-package.json5 中链接本地 HAR 包,并使用 overrides 确保所有模块引用的 RNOH 版本一致。
{
"modelVersion": "6.0.2",
"description": "Please describe the basic information.",
"dependencies": {
"@rnoh/react-native-openharmony": "0.72.90",
"@react-native-oh-tpl/react-native-safe-area-context": "file:../node_modules/@react-native-oh-tpl/react-native-safe-area-context/harmony/safe_area.har",
"@react-native-oh-tpl/react-native-gesture-handler": "file:../node_modules/@react-native-oh-tpl/react-native-gesture-handler/harmony/gesture_handler.har",
"@react-native-oh-tpl/react-native-screens": "file:../node_modules/@react-native-oh-tpl/react-native-screens/harmony/screens.har"
},
"overrides": {
"@rnoh/react-native-openharmony": "0.72.90"
},
"devDependencies": {
"@ohos/hypium": "1.0.25",
"@ohos/hamock": "1.0.0"
},
"dynamicDependencies": {}
}
4.2 第二步:C++ 源码链接 (CMake)
在 harmony/entry/src/main/cpp/CMakeLists.txt 中引入三方库的 C++ 部分。
project(rnapp)
cmake_minimum_required(VERSION 3.28)
# 核心修复:强制 Ninja 将超长参数写入临时 .rsp 文件,绕过 Windows 命令行长度限制
set(CMAKE_NINJA_FORCE_RESPONSE_FILE "ON" CACHE BOOL "Force response file usage" FORCE)
set(CMAKE_SKIP_BUILD_RPATH TRUE)
set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../oh_modules")
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(RNOH_CPP_DIR "${OH_MODULE_DIR}/@rnoh/react-native-openharmony/src/main/cpp")
set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated")
set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
add_compile_definitions(WITH_HITRACE_SYSTRACE)
set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
# 包含 codegen 生成的源码
file(GLOB_RECURSE GENERATED_SRC "${RNOH_GENERATED_DIR}/*.cpp")
# RNOH_BEGIN: add_package_subdirectories
add_subdirectory("${OH_MODULE_DIR}/@react-native-oh-tpl/react-native-safe-area-context/src/main/cpp" ./safe_area)
add_subdirectory("${OH_MODULE_DIR}/@react-native-oh-tpl/react-native-gesture-handler/src/main/cpp" ./gesture_handler)
add_subdirectory("${OH_MODULE_DIR}/@react-native-oh-tpl/react-native-screens/src/main/cpp" ./screens)
# RNOH_END: add_package_subdirectories
add_library(rnoh_app SHARED
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
"./PackageProvider.cpp"
${GENERATED_SRC}
)
target_include_directories(rnoh_app PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(rnoh_app PUBLIC rnoh)
# RNOH_BEGIN: link_packages
target_link_libraries(rnoh_app PUBLIC rnoh_safe_area)
target_link_libraries(rnoh_app PUBLIC rnoh_gesture_handler)
target_link_libraries(rnoh_app PUBLIC rnoh_screens)
# RNOH_END: link_packages
4.3 第三步:Codegen 自动生成胶水代码
运行 Codegen 工具,根据 JS 定义自动生成 C++ 的 TurboModule 声明和 JSI 绑定代码。
npx react-native codegen-harmony --rnoh-module-path ./harmony/oh_modules/@rnoh/react-native-openharmony
生成产物 :位于 harmony/entry/src/main/cpp/generated。
Pasted image 20260420230702.png
4.4 第四步:原生包注册 (PackageProvider.cpp)
文件参考 :PackageProvider.cpp 在 C++ 侧同时注册 Codegen 生成包 和 三方库原生包。
#include "RNOH/PackageProvider.h"
#include "generated/RNOHGeneratedPackage.h"
#include "SafeAreaViewPackage.h"
#include "RnohReactNativeHarmonyGestureHandlerPackage.h"
#include "RnohReactNativeHarmonyScreensPackage.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {
std::make_shared<RNOHGeneratedPackage>(ctx),
std::make_shared<SafeAreaViewPackage>(ctx),
std::make_shared<RnohReactNativeHarmonyGestureHandlerPackage>(ctx),
std::make_shared<RnohReactNativeHarmonyScreensPackage>(ctx)
};
}
4.5 第五步:ArkTS 工厂注册 (RNPackagesFactory.ets)
文件参考 :RNPackagesFactory.ets 在 ArkTS 侧导出 Package 实例。
import { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony/ts';
import { SafeAreaViewPackage } from '@react-native-oh-tpl/react-native-safe-area-context/ts';
import RNGestureHandlerPackage from '@react-native-oh-tpl/react-native-gesture-handler';
import RNOHScreensPackage from '@react-native-oh-tpl/react-native-screens';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [
new SafeAreaViewPackage(ctx),
new RNGestureHandlerPackage(ctx),
new RNOHScreensPackage(ctx)
];
}
章节五:白名单与组件构建器
在 C-API 架构下,某些组件仍需通过 ArkTS 渲染,必须在 Index.ets 中显式声明。
5.1 ArkTS 组件白名单
在 arkTsComponentNames 数组中填入需要 ArkTS 参与渲染的组件名(如 RNSScreenStack)。
import {
AnyJSBundleProvider,
ComponentBuilderContext,
FileJSBundleProvider,
MetroJSBundleProvider,
ResourceJSBundleProvider,
RNApp,
RNOHErrorDialog,
RNOHLogger,
TraceJSBundleProviderDecorator,
RNOHCoreContext
} from '@rnoh/react-native-openharmony';
import { createRNPackages } from '../RNPackagesFactory';
import { SAFE_AREA_VIEW_TYPE, SafeAreaView, SAFE_AREA_PROVIDER_TYPE, SafeAreaProvider } from "@react-native-oh-tpl/react-native-safe-area-context";
import { componentBuilder as RNScreensComponentBuilder } from '@react-native-oh-tpl/react-native-screens';
@Builder
export function buildCustomRNComponent(ctx: ComponentBuilderContext) {
if (ctx.componentName === SAFE_AREA_VIEW_TYPE) {
SafeAreaView({
ctx: ctx.rnComponentContext,
tag: ctx.tag,
buildCustomComponent: buildCustomRNComponent
})
} else if (ctx.componentName === SAFE_AREA_PROVIDER_TYPE) {
SafeAreaProvider({
ctx: ctx.rnComponentContext,
tag: ctx.tag,
buildCustomComponent: buildCustomRNComponent
})
} else {
RNScreensComponentBuilder(ctx)
}
}
const wrappedCustomRNComponentBuilder = wrapBuilder(buildCustomRNComponent)
@Entry
@Component
struct Index {
@StorageLink('RNOHCoreContext') private rnohCoreContext: RNOHCoreContext | undefined = undefined
@State shouldShow: boolean = false
private logger!: RNOHLogger
aboutToAppear() {
this.logger = this.rnohCoreContext!.logger.clone("Index")
const stopTracing = this.logger.clone("aboutToAppear").startTracing();
this.shouldShow = true
stopTracing();
}
onBackPress(): boolean | undefined {
// NOTE: this is required since `Ability`'s `onBackPressed` function always
// terminates or puts the app in the background, but we want Ark to ignore it completely // when handled by RN this.rnohCoreContext!.dispatchBackPress()
return true
}
build() {
Column() {
if (this.rnohCoreContext && this.shouldShow) {
if (this.rnohCoreContext?.isDebugModeEnabled) {
RNOHErrorDialog({ ctx: this.rnohCoreContext })
}
RNApp({
rnInstanceConfig: {
createRNPackages,
enableNDKTextMeasuring: true, // 该项必须为true,用于开启NDK文本测算
enableBackgroundExecutor: false,
enableCAPIArchitecture: true, // 该项必须为true,用于开启CAPI
arkTsComponentNames: [
SAFE_AREA_VIEW_TYPE,
SAFE_AREA_PROVIDER_TYPE,
"RNSScreen",
"RNSScreenContainer",
"RNSScreenNavigationContainer",
"RNSScreenStack",
"RNSScreenStackHeaderConfig",
"RNSScreenStackHeaderSubview",
"RNSFullWindowOverlay",
"RNSModalScreen",
"RNSSearchBar"
]
},
initialProps: { "foo": "bar" } as Record<string, string>,
appKey: "rn-navigation",
wrappedCustomRNComponentBuilder: wrappedCustomRNComponentBuilder,
onSetUp: (rnInstance) => {
rnInstance.enableFeatureFlag("ENABLE_RN_INSTANCE_CLEAN_UP")
},
jsBundleProvider: new TraceJSBundleProviderDecorator(
new AnyJSBundleProvider([
new MetroJSBundleProvider(),
// NOTE: to load the bundle from file, place it in
// `/data/app/el2/100/base/com.rnoh.tester/files/bundle.harmony.js` // on your device. The path mismatch is due to app sandboxing on OpenHarmony new FileJSBundleProvider('/data/storage/el2/base/files/bundle.harmony.js'),
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'hermes_bundle.hbc'),
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js')
]),
this.rnohCoreContext.logger),
})
}
} .height('100%')
.width('100%')
}
}
5.2 委托构建器
使用三方库提供的 componentBuilder 来处理这些组件的创建。
if (ctx.componentName === "RNSScreenStack") {
RNScreensComponentBuilder(ctx);
}
总结:集成全景图
- JS 层:安装 TPL 依赖,编写业务代码。
- 构建层:运行 Codegen 生成 C++ 桥接文件。
- C++ 层 :CMake 链接源码,
PackageProvider注册生成的包和三方库包。 - ArkTS 层 :
RNPackagesFactory导出实例,Index.ets配置白名单和构建器。
掌握了这套路线,你就能在 HarmonyOS 环境中游刃有余地集成任何复杂的 React Native 三方库。