鸿蒙RN搭建工程使用TurboModule
鸿蒙RN搭建工程使用TurboModule
step 1 创建RN工程,然后手动创建harmony工程在根目录下
npx react-native@0.77.1 init awesome --version 0.77.1
之前这种创建工程的方式已经提示被废弃
# 2. 用社区 CLI 指定模板版本
npx @react-native-community/cli@latest init awesome --version 0.77.1
为了使用加速 npm 包的下载,可以配置镜像源;如果关闭 SSL 证书校验还可以进一步加速下载,但是这会降低安全性,需用户评估后再使用
~/.npmrc 原来是
registry=https://registry.npmmirror.com/
改成了
strict-ssl=false
sslVerify=false
registry=https://repo.huaweicloud.com/repository/npm/
安装依赖
npm i
npm i @react-native-oh/react-native-harmony@0.77.18-1
npm i -D @react-native-oh/react-native-harmony-cli@0.77.18
npm install 无响应解决
# 彻底清理
rm -rf node_modules package-lock.json
# 检查 npm 配置
npm config get registry
# 如果需要,设置为华为云镜像(从 package-lock.json 看已经在使用)
npm config set registry https://repo.huaweicloud.com/repository/npm/
# 安装依赖
npm install
通过DevEco Studio 在当前目录下创建harmony 工程
harmony/oh-package.json5 修改,添加react_native_openharmony.har 依赖,外部工程npm安装依赖后 node_modules下就有了
{
"modelVersion": "6.0.0",
"description": "Please describe the basic information.",
"dependencies": {
"@rnoh/react-native-openharmony": "file:../node_modules/@react-native-oh/react-native-harmony/react_native_openharmony.har"
},
"overrides": {
"@rnoh/react-native-openharmony": "file:../node_modules/@react-native-oh/react-native-harmony/react_native_openharmony.har",
},
"devDependencies": {
"@ohos/hypium": "1.0.24",
"@ohos/hamock": "1.0.0"
}
}
harmony/entry/oh-package.json5 下添加react_native_openharmony.har依赖
{
"name": "entry",
"version": "1.0.0",
"description": "Please describe the basic information.",
"main": "",
"author": "",
"license": "",
"dependencies": {
"@rnoh/react-native-openharmony": "file:../../node_modules/@react-native-oh/react-native-harmony/react_native_openharmony.har"
}
}
修改后在DevEco Studio下去同步一下工程
harmony工程下== harmony/entry/src/main/ets/entryability/EntryAbility.ets==
改成继承RNAbility
export default class EntryAbility extends RNAbility {
protected getPagePath(): string {
return "pages/Index"
}
override onCreate(want: Want) {
super.onCreate(want)
hilog.info(0x0000, 'testTag', '%{public}s', 'EntryAbility onCreate');
}
}
添加C++文件 harmony/entry/src/main/cpp/PackageProvider.cpp
#include "RNOH/PackageProvider.h"
#include "RNOH/generated/BaseRtnCalculatorPackage.h"
#include "generated/RNOHGeneratedPackage.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {
//std::make_shared<RNOHGeneratedPackage>(ctx),
//std::make_shared<BaseRtnCalculatorPackage>(ctx)
};
}
新增文件 harmony/entry/src/main/ets/RNPackagesFactory.ets
import { RNPackage, RNPackageContext } from "@rnoh/react-native-openharmony";
import GeneratedPackage from "./GeneratedPackage";
export function createRNPackages(ctx:RNPackageContext) :RNPackage[]{
return [
//new GeneratedPackage(ctx)
];
}
新增文件harmony/entry/src/main/ets/GeneratedPackage.ets
import {
RNOHPackage,
AnyThreadTurboModule,
AnyThreadTurboModuleContext,
UITurboModule,
UITurboModuleContext
} from '@rnoh/react-native-openharmony';
import { TM } from "@rnoh/react-native-openharmony/generated"
import { CalculatorModule } from './turbomodule/CalculatorModule';
export default class GeneratedPackage extends RNOHPackage {
override getUITurboModuleFactoryByNameMap(): Map<string, (ctx: UITurboModuleContext) => UITurboModule | null> {
return new Map<string, ((ctx: UITurboModuleContext) => UITurboModule)>()
.set(TM.RTNCalculator.NAME, (ctx) => new CalculatorModule(ctx)) //这里先返回空的map就可以了
}
override async createEagerUITurboModuleByNameMap(ctx: UITurboModuleContext): Promise<Map<string, UITurboModule>> {
return new Map().set(TM.RTNCalculator.NAME, new CalculatorModule(ctx)) //这里先返回空的map就可以了
}
}
修改文件 harmony/entry/src/main/ets/pages/Index.ets
import {
AnyJSBundleProvider,
ComponentBuilderContext,
FileJSBundleProvider,
MetroJSBundleProvider,
RNApp,
RNOHErrorDialog,
RNOHLogger,
TraceJSBundleProviderDecorator,
RNOHCoreContext,
ResourceJSBundleProvider
} from '@rnoh/react-native-openharmony'
import font from '@ohos.font'
import { createRNPackages } from '../RNPackagesFactory';
// import { componentBuilder } from "@react-native-ohos/react-native-screens"
// import { LottieAnimationView, LOTTIE_TYPE } from "@react-native-ohos/lottie-react-native";
import { KeyboardAvoidMode } from '@kit.ArkUI';
const arkTsComponentNames: Array<string> = []
@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(): void {
this.logger = this.rnohCoreContext!.logger.clone("Index")
const stopTracing = this.logger.clone("aboutToAppear").startTracing()
// for (const customFont of fonts) {
// font.registerFont(customFont)
// }
this.shouldShow = true
stopTracing()
}
onBackPress(): boolean | void {
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: arkTsComponentNames
},
initialProps: { "foo": "bar" } as Record<string, string>,
appKey: "awesome",//这个名称一定要和RNOH工程package.json 中的name保持一致
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%')
}
}
step 2
添加package.json. scripts节点。 这个bundle-harmony工具在react-native-harmony-cli里面,上面已经安装了依赖
"start": "hdc rport tcp:8081 tcp:8081 && react-native start",
"codegen": "react-native codegen-harmony --cpp-output-path ./harmony/entry/src/main/cpp/generated --rnoh-module-path ./harmony/entry/oh_modules/@rnoh/react-native-openharmony",
"dev": "npm run codegen && react-native bundle-harmony --dev",
package.json 下添加节点 harmony 用来codegen使用
"harmony": {
"alias": "rtn-calculator",
"codegenConfig": [
{
"version": 1,
"specPaths": [
"src/specs/v1"
]
},
{
"version": 2,
"specPaths": [
"src/specs/v2"
]
}
]
},
"files": [
"index.ts",
"src/*"
],
harmony/entry/build-profile.json5 buildOption 里面添加
"externalNativeOptions": {
"path": "./src/main/cpp/CMakeLists.txt",
"arguments": "",
"cppFlags": "",
},
添加 CMakeLists.txt文件 harmony/entry/src/main/cpp/CMakeLists.txt
project(rnapp)
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on) #给 生成构建系统(ninja / make)用的开关:ON 时 会把 完整编译命令 全部打印出来:[1/2] /path/to/clang++ -DWITH_HITRACE_SYSTRACE -I../../generated -std=c++17 ... -c xxx.cpp -o xxx.cpp.o; 默认 OFF 时 你执行 ninja / make 看到的只是简短一行:[1/2] Building CXX object CMakeFiles/xxx.cpp.o
set(WITH_HITRACE_SYSTRACE 1) #产生一个 CMake 变量,供 if(WITH_HITRACE_SYSTRACE) 这类 脚本逻辑 使用
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}") #必须配置,内部用到了这个变量名,所以名字不能改
#set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules")
set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../oh_modules")#定义oh_modules路径,方便其他变量简写
set(RNOH_CPP_DIR "${OH_MODULES}/@rnoh/react-native-openharmony/src/main/cpp") #必须配置
set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated/awasome") #必须配置。awasome 是根据生成的目录名称改变
#file(GLOB GENERATED_CPP_FILES "./generated/*.cpp")
#set(rtn_calculator_generated_dir "${RNOH_GENERATED_DIR}/rtn_calculator")
file(GLOB_RECURSE rtn_calculator_generated_dir_src "${RNOH_GENERATED_DIR}/**/*.cpp")#当前目录 + 所有子目录,收集多层目录里生成的代码
file(GLOB rtn_calculator_package_src CONFIGURE_DEPENDS *.cpp)#收集同目录手写源文件,搜索范围为当前 CMakeLists.txt 所在目录 CONFIGURE_DEPENDS 让 “同目录通配符” 也能增量感知文件增减,只对 GLOB 有效,GLOB_RECURSE 本身已自带目录树监控,再加会被忽略。
add_compile_definitions(WITH_HITRACE_SYSTRACE) #CMake 3.12 起提供的命令,等价于在 所有编译器命令行 末尾追加 -DWITH_HITRACE_SYSTRACE,也就是定义一个 布尔宏:-DWITH_HITRACE_SYSTRACE
add_subdirectory("${RNOH_CPP_DIR}" ./rn) #把 RN 引擎(C++ 源码树) 整个拉进来一起编译,并告诉 CMake:源码在 ${RNOH_CPP_DIR},生成的中间产物(build artifacts)放到当前二进制目录下的 ./rn 子文件夹里
add_library(rnoh_app SHARED
# ${GENERATED_CPP_FILES}
${rtn_calculator_package_src}
${rtn_calculator_generated_dir_src}
# "./PackageProvider.cpp"
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)
target_include_directories(rnoh_app PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${RNOH_GENERATED_DIR}
${RNOH_GENERATED_DIR}/rtn_calculator
)
target_link_libraries(rnoh_app PUBLIC rnoh) #target_link_libraries(<目标> <可见性> <库>...)这一行把 “rnoh” 这个静态库 真正挂到最终动态库 librnoh_app.so 上,并告诉编译器:所有依赖 rnoh_app 的目标,也能 自动拿到 rnoh 的头文件搜索路径和链接标志
打开项目根目录下metro.config.js,并添加 OpenHarmony 的适配代码。配置文件的详细介绍,可以参考++React Native 中文网++。修改完成后的文件内容如下:
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);
目录下运行生成 bundle 文件的命令。运行成功后,会在 AwesomeProject/harmony/entry/src/main/resources/rawfile 目录下生成 bundle.harmony.js 和 assets 文件夹,assets 用来存放图片(如果 bundle 中不涉及本地图片,则没有 assets 文件夹)。
清理demo中用到的组件,只展示一个Text,不然运行下面会报错找不到Image 等组件,默认是RN官方的demo界面
npm run dev
警告信息处理
WARN Watchman `watch-project` returned a warning: Recrawled this watch 27 times, most recently because:
MustScanSubDirs UserDroppedTo resolve, please review the information on
https://facebook.github.io/watchman/docs/troubleshooting.html#recrawl
To clear this warning, run:
`watchman watch-del '/Users/fangzhen/Documents/react_native/example/awesome' ; watchman watch-project '/Users/fangzhen/Documents/react_native/example/awesome'`
WARN Watchman `query` returned a warning: Recrawled this watch 27 times, most recently because:
MustScanSubDirs UserDroppedTo resolve, please review the information on
https://facebook.github.io/watchman/docs/troubleshooting.html#recrawl
To clear this warning, run:
`watchman watch-del '/Users/fangzhen/Documents/react_native/example/awesome' ; watchman watch-project '/Users/fangzhen/Documents/react_native/example/awesome'`
watchman watch-del '/Users/fangzhen/Documents/react_native/example/awesome'
watchman watch-project '/Users/fangzhen/Documents/react_native/example/awesome'
awesome/src/specs/v2/NativeCalculator.ts RTNCalculator就是导出模块的名称,后续CPP,ArkTS 模块里面都是这个名称
import {TurboModuleRegistry} from 'react-native';
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
export interface Spec extends TurboModule {
add(a: number, b: number): Promise<number>;
}
export default TurboModuleRegistry.get<Spec>('RTNCalculator') as Spec | null;
step 3
fangzhen@fangzhens-MacBook-Pro awesome % npm run codegen
> awesome@0.0.1 codegen
> react-native codegen-harmony --cpp-output-path ./harmony/entry/src/main/cpp/generated --rnoh-module-path ./harmony/entry/oh_modules/@rnoh/react-native-openharmony
• harmony/entry/oh_modules/@rnoh/react-native-openharmony/generated/components/ts.ts
• harmony/entry/oh_modules/@rnoh/react-native-openharmony/generated/index.ets
• harmony/entry/oh_modules/@rnoh/react-native-openharmony/generated/ts.ts
• harmony/entry/oh_modules/@rnoh/react-native-openharmony/generated/turboModules/RTNCalculator.ts
• harmony/entry/oh_modules/@rnoh/react-native-openharmony/generated/turboModules/ts.ts
• harmony/entry/src/main/cpp/generated/RNOHGeneratedPackage.h
• harmony/entry/src/main/cpp/generated/rtn_calculator/RNOH/generated/BaseRtnCalculatorPackage.h
• harmony/entry/src/main/cpp/generated/rtn_calculator/RNOH/generated/turbo_modules/RTNCalculator.cpp
• harmony/entry/src/main/cpp/generated/rtn_calculator/RNOH/generated/turbo_modules/RTNCalculator.h
• harmony/entry/src/main/cpp/generated/rtn_calculator/react/renderer/components/rtn_calculator/ComponentDescriptors.h
• harmony/entry/src/main/cpp/generated/rtn_calculator/react/renderer/components/rtn_calculator/EventEmitters.cpp
• harmony/entry/src/main/cpp/generated/rtn_calculator/react/renderer/components/rtn_calculator/EventEmitters.h
• harmony/entry/src/main/cpp/generated/rtn_calculator/react/renderer/components/rtn_calculator/Props.cpp
• harmony/entry/src/main/cpp/generated/rtn_calculator/react/renderer/components/rtn_calculator/Props.h
• harmony/entry/src/main/cpp/generated/rtn_calculator/react/renderer/components/rtn_calculator/ShadowNodes.cpp
• harmony/entry/src/main/cpp/generated/rtn_calculator/react/renderer/components/rtn_calculator/ShadowNodes.h
• harmony/entry/src/main/cpp/generated/rtn_calculator/react/renderer/components/rtn_calculator/States.cpp
• harmony/entry/src/main/cpp/generated/rtn_calculator/react/renderer/components/rtn_calculator/States.h
info Generated 18 file(s)
fangzhen@fangzhens-MacBook-Pro awesome %
harmony/entry/src/main/ets/turbomodule/CalculatorModule.ets
import { UITurboModule,AnyThreadTurboModule } from '@rnoh/react-native-openharmony';
import { TM } from '@rnoh/react-native-openharmony/generated/ts';
export class CalculatorModule extends UITurboModule implements TM.RTNCalculator.Spec {
say(): string {
return "Hello World 123 123"
}
add(a: number, b: number): Promise<number>{
// this.ctx.rnInstance.emitDeviceEvent("clickMarqueeEvent", { params: { age: 18 } })
return Promise.resolve(a+b);
}
}
生成的c++代码中可以自定义写法,下面的例子,没有调用arkts里面的say函数实现,而是直接使用c++原生实现返回JSI DEMO字符串,重新实现了add函数,返回promise
/**
* This code was generated by "react-native codegen-harmony"
*
* Do not edit this file as changes may cause incorrect behavior and will be
* lost once the code is regenerated.
*
* @generatorVersion: 2
*/
#include "RTNCalculator.h"
namespace rnoh {
using namespace facebook;
static jsi::Value __hostFunction_RTNCalculatorCxxModuleSpecJSI_say(jsi::Runtime &rt, react::TurboModule &turboModule,
const jsi::Value *args, size_t count) {
const char * str = "JSI DEMO";
return jsi::String::createFromUtf8(rt, str);
}
static jsi::Value __hostFunction_RTNCalculatorCxxModuleSpecJSI_add(jsi::Runtime &rt, react::TurboModule &turboModule,
const jsi::Value *args, size_t count) {
double a = args[0].getNumber();
double b = args[1].getNumber();
// 获取全局Promise构造函数
jsi::Object global = rt.global();
jsi::Function promiseCtor = global.getPropertyAsFunction(rt, "Promise");
// 创建executor函数
jsi::Function executor = jsi::Function::createFromHostFunction(
rt,
jsi::PropNameID::forAscii(rt, "executor"),
2, // resolve和reject参数
[a, b](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *executorArgs, size_t executorCount) -> jsi::Value {
// 获取resolve函数
jsi::Function resolve = executorArgs[0].asObject(rt).asFunction(rt);
// 执行计算
double result = (a + b)*100;
// 调用resolve
resolve.call(rt, jsi::Value(rt, result));
return jsi::Value::undefined();
}
);
// 创建并返回Promise实例
return promiseCtor.callAsConstructor(rt, executor);
}
简写
static jsi::Value __hostFunction_RTNCalculatorCxxModuleSpecJSI_add(jsi::Runtime &rt, react::TurboModule &turboModule,
const jsi::Value *args, size_t count) {
double a = args[0].getNumber();
double b = args[1].getNumber();
return rt.global()
.getPropertyAsFunction(rt, "Promise")
.callAsConstructor(rt, jsi::Function::createFromHostFunction(
rt, jsi::PropNameID::forAscii(rt, ""), 2,
[a, b](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *execArgs, size_t) {
execArgs[0].asObject(rt).asFunction(rt).call(rt, jsi::Value(rt, a + b));
return jsi::Value::undefined();
}));
}
RTNCalculator::RTNCalculator(const ArkTSTurboModule::Context ctx, const std::string name)
: ArkTSTurboModule(ctx, name) {
methodMap_ = {
// ARK_ASYNC_METHOD_METADATA(add, 2),
// ARK_METHOD_METADATA(say, 0),
};
methodMap_["say"] = MethodMetadata{0, __hostFunction_RTNCalculatorCxxModuleSpecJSI_say};
methodMap_["add"] = MethodMetadata{2, __hostFunction_RTNCalculatorCxxModuleSpecJSI_add};
}
} // namespace rnoh
RN:TurboModule 组件。 Fabric 组件、Autolinking
更多推荐



所有评论(0)