上一篇文章說下載鴻蒙開發(fā)工具,今天開始實踐了
1. 新建rn項目(mac端)
npx react-native@0.72.5 init AwesomeProject --version 0.72.5
或者使用如下命令,跳過其他node_modules的安裝過程,因為暫時不需要依賴庫,但是網(wǎng)速好的情況下,還是建議用上面的命令,一次安裝成功
npx react-native@0.72.5 init AwesomeProject --version 0.72.5 --skip-install
init之后的文件夾如圖所示,這里只有ios和android文件夾,我的理解應(yīng)該有個harmony文件夾放著鴻蒙的項目才對,讓我們接著往下看

image.png
2.下載安裝鴻蒙依賴
在react native項目的package.json文件內(nèi)添加"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到新建項目的文件下,執(zhí)行
// @x.x.x可以不要,默認(rèn)會安裝最新版的依賴,也可以指定版本號,但是要考慮跟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對應(yīng)的文件夾下,運行npm run dev 即運行npm run react-native bundle-harmony --dev
運行完成會生成bundle.harmony.js和assets文件夾,如下圖

131751370932_.pic.jpg
5. 新建鴻蒙項目
這里不再過多介紹,鴻蒙開發(fā)官網(wǎng)都有介紹,注意一點:在創(chuàng)建項目的時候,bundlename要與開發(fā)者證書里的bundlename一致,可以在DevEco Studio -> file -> project structure中查看bundle name,如下圖

image.png
然后在確定下證書中對應(yīng)的bundle name,終端輸入命令:
keytool -list -v -keystore debug2.p12 -storetype PKCS12
image.png
我這里隨便找的一個證書實驗的,所以bundle name是空,bundle name是
所有者: CN=test5566, OU=, O=, L=, ST=, C=中的O=對應(yīng)的東西。
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 中的代碼添加到鴻蒙的編譯構(gòu)建任務(wù)中
{
"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的參數(shù)appKey需要與RN工程中AppRegistry.registerComponent注冊的appName保持一致,否則會導(dǎo)致白屏。
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 中使用
最后一步,運行鴻蒙,一定要連接手機,不然會報錯