项目概述与基础信息

  • 项目名称: 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/JSVM
  • onBackPress 将返回键事件转发给 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.cppRNOHAppNapiBridge.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.registerComponentindex.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 负责写入模块表;__rmoduleId 加载模块并返回 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)

欢迎加入开源鸿蒙跨平台社区

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐