上一篇文章说下载鸿蒙开发工具,今天开始实践了
1. 新建rn项目(mac端)
npx react-native@0.72.5 init AwesomeProject --version 0.72.5
或者使用如下命令,跳过其他node_modules的安装过程,因为暂时不需要依赖库,但是网速好的情况下,还是建议用上面的命令,一次安装成功
npx react-native@0.72.5 init AwesomeProject --version 0.72.5 --skip-install
init之后的文件夹如图所示,这里只有ios和android文件夹,我的理解应该有个harmony文件夹放着鸿蒙的项目才对,让我们接着往下看

image.png
2.下载安装鸿蒙依赖
在react native项目的package.json文件内添加"dev": "react-native bundle-harmony --dev"这句话
{
"name": "AwesomeProject",
"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",
+ "dev": "react-native bundle-harmony --dev"
},
"dependencies": {
"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"
}
}
打开terminal,cd到新建项目的文件下,执行
// @x.x.x可以不要,默认会安装最新版的依赖,也可以指定版本号,但是要考虑跟rn版本的兼容性
npm i @react-native-oh/react-native-harmony@x.x.x
// 我这里用的是npm i @react-native-oh/react-native-harmony@0.72.53
3. 修改rn配置文件metro.config.js
const {mergeConfig, getDefaultConfig} = require('@react-native/metro-config');
const {createHarmonyMetroConfig} = require('@react-native-oh/react-native-harmony/metro.config');
/**
* @type {import("metro-config").ConfigT}
*/
const config = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
};
module.exports = mergeConfig(getDefaultConfig(__dirname), createHarmonyMetroConfig({
reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony',
}), config);
4. 生成bundle文件,供鸿蒙使用
cd到rn对应的文件夹下,运行npm run dev 即运行npm run react-native bundle-harmony --dev
运行完成会生成bundle.harmony.js和assets文件夹,如下图

131751370932_.pic.jpg
5. 新建鸿蒙项目
这里不再过多介绍,鸿蒙开发官网都有介绍,注意一点:在创建项目的时候,bundlename要与开发者证书里的bundlename一致,可以在DevEco Studio -> file -> project structure中查看bundle name,如下图

image.png
然后在确定下证书中对应的bundle name,终端输入命令:
keytool -list -v -keystore debug2.p12 -storetype PKCS12
image.png
我这里随便找的一个证书实验的,所以bundle name是空,bundle name是
所有者: CN=test5566, OU=, O=, L=, ST=, C=中的O=对应的东西。
6,修改bundle name(非必需)
找到AppScope/app.json5
{
"app": {
"bundleName": "com.123.app.id",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:app_layered_image",
"label": "$string:app_name"
}
}
在这里修改之后,build clean,然后rebuild即可
7,在 MyApplication/entry/src/main 目录下新建 cpp 文件夹。在 cpp 目录下新增 CMakeLists.txt
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
"./PackageProvider.cpp"
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)
target_link_libraries(rnoh_app PUBLIC rnoh)
8,在 cpp 目录下新增 PackageProvider.cpp
#include "RNOH/PackageProvider.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {};
}
9,打开 MyApplicaton\entry\build-profile.json5,将 cpp 中的代码添加到鸿蒙的编译构建任务中
{
"apiType": "stageMode",
"buildOption": {
+ "externalNativeOptions": {
+ "path": "./src/main/cpp/CMakeLists.txt",
+ "arguments": "",
+ "cppFlags": "",
+ }
},
"buildOptionSet": [
{
"name": "release",
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": true,
"files": [
"./obfuscation-rules.txt"
]
}
}
}
},
],
"targets": [
{
"name": "default"
},
{
"name": "ohosTest",
}
]
}
10, 打开 MyApplicaton\entry\src\main\ets\entryability\EntryAbility.ets,引入并使用 RNAbility,该文件需要满足以下的要求:
import { RNAbility } from '@rnoh/react-native-openharmony';
export default class EntryAbility extends RNAbility {
getPagePath() {
return 'pages/Index';
}
}
11, 在 MyApplicaton\entry\src\main\ets 目录下新增 RNPackagesFactory.et
import { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [];
}
12,打开 MyApplicaton\entry\src\main\ets\pages\Index.ets,添加RNOH的使用代码,修改后如下:
RNApp的参数appKey需要与RN工程中AppRegistry.registerComponent注册的appName保持一致,否则会导致白屏。
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: "AwesomeProject",
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%')
}
}
13,加载bundle包,本地加载 bundle。将 bundle 文件和 assets 图片放在 entry/src/main/resources/rawfile 路径下,在 entry/src/main/ets/pages/Index.ets 中使用
最后一步,运行鸿蒙,一定要连接手机,不然会报错