React Native for OpenHarmony开源鸿蒙侧DailyHot壳工程深度解读
·
项目概述与基础信息
- 项目名称: DailyHot
- 包名:
com.example.rnoh.dailyhot - 版本: 1.0.0 (versionCode: 1000000)
- 目标/兼容 SDK: 6.0.0(20)
- 运行时系统: HarmonyOS
- 技术特点: Stage 模型 + ArkUI + React Native OpenHarmony(RNOH)
- RN侧:DailyHotBundle
技术架构与项目结构
技术栈与应用模型
- ArkTS: 类型安全、现代化;主语言
- C++/NAPI: 原生模块扩展,高性能/系统能力
- ArkUI: 声明式 UI 框架
- RNOH: 复用 React Native 生态,跨平台能力
- Stage 模型: 更佳的生命周期/资源管理
目录结构总览
DailyHot/
├── AppScope/ # 应用级配置/资源
│ ├── app.json5
│ └── resources/base/{element,media}
├── entry/ # 主模块
│ ├── src/main/ets/ # ArkTS
│ │ ├── entryability/EntryAbility.ets
│ │ ├── entrybackupability/EntryBackupAbility.ets
│ │ └── pages/Index.ets
│ ├── src/main/cpp/ # 原生
│ │ ├── CMakeLists.txt
│ │ ├── napi_init.cpp
│ │ └── PackageProvider.cpp
│ ├── src/main/resources/
│ │ └── rawfile/bundle.harmony.js # RN 打包代码
│ ├── src/ohosTest/ # 测试
│ ├── module.json5 # 模块配置
│ ├── build-profile.json5 # 模块构建
│ └── hvigorfile.ts # 模块构建脚本
├── build-profile.json5 # 项目构建
├── hvigorfile.ts # 项目构建脚本
├── oh-package.json5 # 依赖管理
└── local.properties # 本地构建属性
核心模块
主入口 Ability(EntryAbility.ets)
- 负责生命周期:
onCreate初始化、onWindowStageCreate加载页面、onForeground/onBackground前后台切换、onDestroy释放资源
代码(entry/src/main/ets/entryability/EntryAbility.ets:1):
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 {
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 {
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 {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
}
protected getPagePath(): string {
return "pages/Index"
}
}
解读:
- 继承
RNAbility打通 RNOH 能力,支持 React Native 实例生命周期托管 setColorMode以防 UI 模式冲突;异常通过hilog上报,便于定位loadContent('pages/Index')作为 ArkUI 页面入口,失败时日志记录并停止流程getPagePath()指定默认页面路径,便于 RNOH 关联页面渲染
主页面(Index.ets)
- ArkUI 声明式语法;
@Entry/@Component+@State响应式;RelativeContainer布局;$r()引用资源
代码(entry/src/main/ets/pages/Index.ets:1):
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 {
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,
enableBackgroundExecutor: false,
enableCAPIArchitecture: true,
arkTsComponentNames: []
},
initialProps: { } as Record<string, string>,
appKey: "DailyHotBundle",
wrappedCustomRNComponentBuilder: wrappedCustomRNComponentBuilder,
onSetUp: (rnInstance) => {
rnInstance.enableFeatureFlag("ENABLE_RN_INSTANCE_CLEAN_UP")
},
jsBundleProvider: new TraceJSBundleProviderDecorator(
new AnyJSBundleProvider([
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js')
]), this.rnohCoreContext.logger),
})
}
}
.height('100%')
.width('100%')
}
}
解读:
@StorageLink('RNOHCoreContext')共享核心上下文,实现 RN 与 ArkTS 的状态/事件联动RNApp初始化 RN 实例:指定包工厂createRNPackages、启用 NDK 文本测量与 CAPI 架构jsBundleProvider通过资源管理器加载bundle.harmony.js,支持 Hermes/JSVMonBackPress将返回键事件转发给 RN,避免系统默认行为打断 RN 路由栈- Debug 模式下展示
RNOHErrorDialog,便于开发期错误定位
构建与运行(Hvigor/打包/签名)
- 构建工具:项目级
appTasks,模块级hapTasks - 项目构建(build-profile.json5):签名、产品、模式(debug/release)
- 模块构建(entry/build-profile.json5):
apiType: stageMode、资源打包选项、release 混淆规则 - 构建流程:编译 ArkTS → 编译 C++ → 打包资源 → 生成 HAP → 签名
- 调试支持:模拟器/真机、热重载、断点
资源与国际化
- 类型:字符串/颜色/浮点/媒体/配置
- 引用:
$r('app.string.app_name')、$r('app.color.primary')、$r('app.float.xxx')、$media:layered_image - 语言:
base/默认、en_US/英文、zh_CN/中文
原生模块与 RNOH 集成
- 原生(C++/NAPI):
napi_init.cpp初始化、PackageProvider.cpp提供者;适配 TurboModule/桥接层 - RNOH:在 HarmonyOS 上复用 RN 生态组件;
react-native-harmony对接视图配置、事件、命令分发 - RN Bundle:
entry/src/main/resources/rawfile/bundle.harmony.js作为 JS 入口(见下文深度解读)
CMake 构建(entry/src/main/cpp/CMakeLists.txt)
代码(entry/src/main/cpp/CMakeLists.txt:1):
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(DailyHot)
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
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(entry SHARED napi_init.cpp)
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
add_library(rnoh_app SHARED
"./PackageProvider.cpp"
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)
target_link_libraries(entry PUBLIC libace_napi.z.so)
target_link_libraries(rnoh_app PUBLIC rnoh)
解读:
- 目录与宏:
RNOH_CPP_DIR指向 RNOH 框架 C++ 源码;WITH_HITRACE_SYSTRACE启用性能跟踪宏 - 目标库:
entry动态库暴露napi_init.cpp,用于 NAPI 初始化注册rnoh_app动态库包含PackageProvider.cpp与RNOHAppNapiBridge.cpp,连接 RNOH 核心rnoh
- 链接:
entry链接libace_napi.z.so(ArkUI NAPI),rnoh_app链接 RNOH 核心实现 - 结构:通过
add_subdirectory(${RNOH_CPP_DIR} ./rn)引入 RNOH 的构建子树,统一工程配置与生成物路径
PackageProvider(entry/src/main/cpp/PackageProvider.cpp)
代码(entry/src/main/cpp/PackageProvider.cpp:1):
//
// Created on 2025/11/12.
//
// 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 {};
}
解读:
- 作用:
PackageProvider为 RNOH 提供原生包列表,便于注册自定义 C++ 模块/TurboModule;当前返回空列表,表示未自定义扩展包 - 扩展方式:在
getPackages中返回std::make_shared<YourPackage>(ctx)等实例,实现 RN ↔ ArkTS ↔ C++ 的桥接能力 - 依赖关系:与 CMake 中的
rnoh_app目标绑定,编译后由 RNOH 框架在运行期加载并调用,完成原生能力注入
深度解读:bundle.harmony.js(React Native 在 HarmonyOS 的运行时)
文件作用与入口
- 位置:
entry/src/main/resources/rawfile/bundle.harmony.js - 作用:包含 Metro 打包后的 JS 运行时与业务代码,负责模块加载、错误处理、日志桥接、热更新,以及将
App注册到原生渲染体系 - 入口模块注册:
AppRegistry.registerComponent在index.js中完成(bundle.harmony.js:1323–1325)
代码(bundle.harmony.js:1316–1326):
var _interopRequireDefault = _$$_REQUIRE(_dependencyMap[0], "@babel/runtime/helpers/interopRequireDefault");
var _reactNative = _$$_REQUIRE(_dependencyMap[1], "react-native");
var _App = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[2], "./App"));
_reactNative.AppRegistry.registerComponent(
_$$_REQUIRE(_dependencyMap[3], "./app.json").name,
function () { return _App.default; }
);
解读:
- 通过
AppRegistry.registerComponent(app.json.name, () => App)将根组件注册到原生端,驱动 UI 渲染
Metro 运行时与模块系统
- 全局挂载:
global.__r = metroRequire(bundle.harmony.js:5) - 模块定义:
global["__d"] = define(bundle.harmony.js:6, 38–68) - 依赖加载:
metroRequire(moduleId)(bundle.harmony.js:69–97) - 分段机制:
registerSegment/packModuleId/unpackModuleId(bundle.harmony.js:185–199, 200–219)支持代码分段与懒加载 - 加载实现:
loadModuleImplementation(bundle.harmony.js:220–303),含原生nativeRequire回落与错误处理 - 未知模块错误:
unknownModuleError(bundle.harmony.js:304–310)
代码(bundle.harmony.js:1–12, 38–47, 69–81):
var __BUNDLE_START_TIME__=globalThis.nativePerformanceNow?nativePerformanceNow():Date.now(),__DEV__=true;
(function (global) {
"use strict";
global.__r = metroRequire;
global[`${__METRO_GLOBAL_PREFIX__}__d`] = define;
var modules = clear();
function define(factory, moduleId, dependencyMap) {
if (modules.has(moduleId)) { return; }
var mod = { dependencyMap, factory, hasError: false, importedAll: EMPTY, importedDefault: EMPTY, isInitialized: false, publicModule: { exports: {} } };
modules.set(moduleId, mod);
}
function metroRequire(moduleId) {
if (moduleId === null) { throw new Error("Cannot find module"); }
var module = modules.get(moduleId);
return module && module.isInitialized ? module.publicModule.exports : guardedLoadModule(moduleId, module);
}
})((typeof globalThis!=='undefined'?globalThis:typeof global!=='undefined'?global:typeof window!=='undefined'?window:this));
解读:
__d负责写入模块表;__r以moduleId加载模块并返回exports- 已初始化模块直接返回
publicModule.exports;否则进入受保护的加载流程
代码(bundle.harmony.js:185–219):
var ID_MASK_SHIFT = 16;
var LOCAL_ID_MASK = ~0 >>> ID_MASK_SHIFT;
function unpackModuleId(moduleId) {
var segmentId = moduleId >>> ID_MASK_SHIFT;
var localId = moduleId & LOCAL_ID_MASK;
return { segmentId, localId };
}
function packModuleId(value) {
return (value.segmentId << ID_MASK_SHIFT) + value.localId;
}
function registerSegment(segmentId, moduleDefiner, moduleIds) {
moduleDefinersBySegmentID[segmentId] = moduleDefiner;
if (moduleIds) { moduleIds.forEach(function (moduleId) { definingSegmentByModuleID.set(moduleId, segmentId); }); }
}
解读:
- 通过高位
segmentId与低位localId组合moduleId,支持分段/懒加载与原生nativeRequire
代码(bundle.harmony.js:220–239, 240–247, 249–257):
function loadModuleImplementation(moduleId, module) {
if (!module && moduleDefinersBySegmentID.length > 0) {
var segmentId = definingSegmentByModuleID.get(moduleId) ?? 0;
var definer = moduleDefinersBySegmentID[segmentId];
if (definer != null) { definer(moduleId); module = modules.get(moduleId); definingSegmentByModuleID.delete(moduleId); }
}
var nativeRequire = global.nativeRequire;
if (!module && nativeRequire) {
var { segmentId: _segmentId, localId } = unpackModuleId(moduleId);
nativeRequire(localId, _segmentId);
module = modules.get(moduleId);
}
if (!module) { throw unknownModuleError(moduleId); }
if (module.hasError) { throw module.error; }
module.isInitialized = true;
var { factory, dependencyMap } = module;
factory(global, metroRequire, metroImportDefault, metroImportAll, module.publicModule, module.publicModule.exports, dependencyMap);
return module.publicModule.exports;
}
解读:
- 首先尝试分段
definer(moduleId);其次回落到原生nativeRequire(localId, segmentId);最终执行模块factory
错误处理与保护
- 全局错误工具:
ErrorUtils(bundle.harmony.js:1158–1255)提供setGlobalHandler/reportFatalError/applyWithGuard,与guardedLoadModule(bundle.harmony.js:169–184)配合保证加载期稳定性
代码(bundle.harmony.js:1169–1180, 1194–1201, 1202–1219):
var _inGuard = 0;
var _globalHandler = function onError(e, isFatal) { throw e; };
var ErrorUtils = {
setGlobalHandler: function(fun) { _globalHandler = fun; },
getGlobalHandler: function() { return _globalHandler; },
reportFatalError: function(error) { _globalHandler && _globalHandler(error, true); },
applyWithGuard: function(fun, context, args) {
try { _inGuard++; return fun.apply(context, args); }
catch (e) { ErrorUtils.reportError(e); }
finally { _inGuard--; }
return null;
}
};
global.ErrorUtils = ErrorUtils;
解读:
- 通过
applyWithGuard包裹执行,统一异常上报到_globalHandler;与模块加载的guardedLoadModule配套提高健壮性
Console 桥接(日志到原生)
console.*透传到nativeLoggingHook(bundle.harmony.js:1083–1100)- 日志等级映射与格式化:
getNativeLogFunction(bundle.harmony.js:948–983) - 扩展:
console.table/group/groupCollapsed/groupEnd/assert等(bundle.harmony.js:1090–1095, 1057–1068, 1069–1073)
代码(bundle.harmony.js:1083–1096):
global.console = {
error: getNativeLogFunction(LOG_LEVELS.error),
info: getNativeLogFunction(LOG_LEVELS.info),
log: getNativeLogFunction(LOG_LEVELS.info),
warn: getNativeLogFunction(LOG_LEVELS.warn),
trace: getNativeLogFunction(LOG_LEVELS.trace),
debug: getNativeLogFunction(LOG_LEVELS.trace),
table: consoleTablePolyfill,
group: consoleGroupPolyfill,
groupEnd: consoleGroupEndPolyfill,
groupCollapsed: consoleGroupCollapsedPolyfill,
assert: consoleAssertPolyfill
};
解读:
- 重写
console方法以调用nativeLoggingHook;保留开发期增强的表格/分组/断言能力
React Refresh(热更新)
- DEV 运行时:
createHotReloadingObject/metroHotUpdateModule(bundle.harmony.js:319–334, 335–456) - 刷新边界判定/签名:
isReactRefreshBoundary/shouldInvalidateReactRefreshBoundary(bundle.harmony.js:550–587) - 导出注册:
registerExportsForReactRefresh(bundle.harmony.js:608–623)
代码(bundle.harmony.js:319–334):
var createHotReloadingObject = function() {
var hot = { _acceptCallback: null, _disposeCallback: null, _didAccept: false,
accept: function(callback) { hot._didAccept = true; hot._acceptCallback = callback; },
dispose: function(callback) { hot._disposeCallback = callback; }
};
return hot;
};
代码(bundle.harmony.js:550–587):
var shouldInvalidateReactRefreshBoundary = function(Refresh, prevExports, nextExports) {
var prevSignature = getRefreshBoundarySignature(Refresh, prevExports);
var nextSignature = getRefreshBoundarySignature(Refresh, nextExports);
if (prevSignature.length !== nextSignature.length) { return true; }
for (var i = 0; i < nextSignature.length; i++) {
if (prevSignature[i] !== nextSignature[i]) { return true; }
}
return false;
};
解读:
- 模块可接受热更新时通过
hot.accept标记;当导出签名变化时,触发边界失效并回退到整页刷新
业务入口与组件注册
index.js模块:- 引入:
react-native与./App(bundle.harmony.js:1316–1319) - 注册:
AppRegistry.registerComponent(app.json.name, () => App)(bundle.harmony.js:1323–1325)
- 引入:
RNOH 适配层(react-native-harmony)
- Harmony 定制导出:
node_modules/@react-native-oh/react-native-harmony/index.js模块在 bundle 中聚合 RN 能力(bundle.harmony.js:1335–1542) - 关键映射:
- 视图属性/配置注册:
ReactNativeViewConfigRegistry.register(bundle.harmony.js:1531–1539) - 组件:
View/Text/Image/ScrollView/...等导出(bundle.harmony.js:1389–1511) - 模块:
TurboModuleRegistry/NativeModules/UIManager(bundle.harmony.js:1492, 1422, 1494)
- 视图属性/配置注册:
- Patch 示例:
AccessibilityInfo的 Harmony 适配(bundle.harmony.js:1563–1750),通过自定义原生接口实现平台一致性
运行时加载链路(概览)
- 初始化前奏:设置全局、构建模块 Map、DEV 下的 Refresh 注册(bundle.harmony.js:1–37)
- 模块注册:
__d(factory, id, depMap, verboseName)写入模块表 - 调用链:业务入口触发
AppRegistry.registerComponent→ 框架加载根组件 → 通过桥接渲染到 Harmony 原生视图
性能与包体优化建议
- 合理分段:使用
registerSegment(bundle.harmony.js:200–219)降低首包体积 - 避免循环依赖:Metro 在 DEV 会检测并警告(bundle.harmony.js:83–93)
- 关闭 DEV 特性:生产环境构建移除 React Refresh/工况代码,减小体积(bundle.harmony.js:276–279)
- 日志降噪:生产禁用冗余 console 输出;构建时区分
NODE_ENV
调试与问题定位技巧
- 使用
console._isPolyfilled判断是否接管(bundle.harmony.js:1096–1100) - 查看模块注册:
metroRequire.getModules()(bundle.harmony.js:316–318) - 错误追踪:
ErrorUtils.setGlobalHandler自定义全局错误处理(bundle.harmony.js:1188–1193)
欢迎加入开源鸿蒙跨平台社区
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)