鸿蒙 React Native 编译开发

鸿蒙 React Native 编译开发

一、环境搭建

1. 基础设置

本文参考:ohos_react_native/docs/zh-***/环境搭建.md · OpenHarmony-SIG/ohos_react_native - AtomGit | GitCode

本文记录操作步骤以及遇到的错误

  1. 安装 DevEco Studio。
  2. 设置 DevEco Studio 开发环境。

可参考我之前写的文章

开源鸿蒙-Fluuter编译开发-CSDN博客

2. hdc环境配置

hdc 是 OpenHarmony 为开发人员提供的用于调试的命令行工具,鸿蒙 React Native 工程使用 hdc 进行真机调试。hdc 工具通过 OpenHarmony SDK 获取,存放于 SDK 的 toolchains 目录下,请将 {DevEco Studio安装路径}/sdk/{SDK版本}/openharmony/toolchains 的完整路径添加到环境变量P——系统变量的path中。

2.1 windows 环境变量设置方法:

此电脑 —— 属性 —— 高级系统设置 —— 高级 —— 环境变量

  • 编辑系统变量path,添加hdc工具路径。
  • 添加 HDC 端口变量名为:HDC_SERVER_PORT,变量值可设置为任意未被占用的端口,如 7035

2.2. 如何查看如何查看电脑未被占用的端口:

Window系统

命令提示符间接排查

  • 按下 Win+R 输入 cmd 打开命令提示符,输入
    ***stat -ano | findstr "LISTENING"

    该命令会列出所有正在监听的已占用端口。排除这些端口后,剩余端口即为未被占用状态;

  • 若想验证特定端口(如 8080)是否空闲,可输入
    ***stat -ano | findstr ":8080"
    无输出则表明端口未被占用。

macOS系统

打开终端,执行以下命令,打开 .bash_profile 文件。

vi ~/.bash_profile

输入以下内容,在 PATH 路径下添加 HDC 工具路径和添加 HDC_SERVER_PORT 端口信息:

export PATH="/Applications/DevEco-Studio.app/Contents/sdk/{版本路径}
/openharmony/toolchains:$PATH" 
# 按照实际 SDK 安装路径配置,需要选择{显示包内容}

HDC_SERVER_PORT=7035 
launchctl setenv HDC_SERVER_PORT $HDC_SERVER_PORT 
export HDC_SERVER_PORT

3. 配置 CAPI 版本环境变量

当前RN框架提供的 Demo 工程默认为 CAPI 版本,需要配置环境变量 RNOH_C_API_ARCH = 1

在系统变量中点击新建,添加变量名为:RNOH_C_API_ARCH,变量值为 1

4. 编辑用户级 .npmrc 配置文件

为了使用加速 npm 包的下载,可以配置镜像源;如果关闭 SSL 证书校验还可以进一步加速下载,但是这会降低安全性,需用户评估后再使用。配置文件位置在 C:\Users\用户名\.npmrc,如果没有则手动创建。

在这里我选择了在 C:\Users\用户名创建了一个.npmrc文档

.npmrc 配置文件内容如下

strict-ssl=false
sslVerify=false
registry=https://repo.huaweicloud.***/repository/npm/

修改 registry 后需执行 npm cache clean --force 清理缓存,以确保新的 registry 生效。

出现一下警告

npm 之所以会给出这个警告,是因为强制清理理论上可能导致正在进行的其他 npm 操作(比如另一个终端窗口正在安装包)失败或损坏。

清理完成后,可以通过以下命令验证:

npm config get registry

如果返回你之前设置的源(https://repo.huaweicloud.***/repository/npm/),说明配置已经生效。

5. 注意事项

安卓和 iOS 的 React Native 完整环境搭建请参考 React Native 官网搭建开发环境 · React Native 中文网

二、创建React Native工程

1. 创建新项目

1.1 创建前的准备

因为React Native 开发需要依赖 Node.js、JDK、Android Studio(安卓开发)/ Xcode(iOS 开发),以及 Watchman(macOS 推荐)。

所以在创建项目前需要先安装核心依赖

  • Node.js:推荐 v18 或更高版本(官网下载:Node.js 中文网),安装后可通过 node -v 验证。
  • JDK:需安装 JDK 17(React Native 推荐版本),可通过 Android Studio 内置安装(后续步骤说明),或官网下载(https://www.oracle.***/java/technologies/downloads/)
  • Watchman(macOS 必装):用于监听文件变化,提升开发效率。通过 Homebrew 安装:
brew install watchman

Node.js的安装详见这篇文章

2024最新版Node.js下载安装及环境配置教程【保姆级】_nodejs下载-CSDN博客

1.2 创建文件

选择一个目录,例如 D 盘根目录,使用 React Native 内置的命令行工具来创建一个名为 “AwesomeProject” 的新项目。这个命令行工具不需要安装,可以直接用 node 自带的 npx 命令来创建

npx react-native@0.72.5 init AwesomeProject --version 0.72.5

出现警告

这些是 npm 的 废弃(deprecated)警告,不是错误!核心意思是:你安装 yarn 时,它的部分依赖包(不是 yarn 本身)已经被作者标记为「不再维护 / 过时」,建议后续替换为更新的替代包。

1.3 创建完成

2. 安装鸿蒙依赖包并生成bundle

本节中使用的各类文件的版本版本配套关系,可以参考GitCode - 全球开发者的开源社区,开源代码托管平台打开 AwesomeProject 目录下的 package.json,在 scripts 下新增 OpenHarmony 的依赖:

可以直接使用这个链接的文件,找到对应文件进行一一替换,修改版本信息并运行。ohos_react_native/docs/zh-***/环境搭建.md · OpenHarmony-SIG/ohos_react_native - AtomGit | GitCode

下载并安装鸿蒙化依赖

2.1 打开 AwesomeProject 目录下的 package.json,在 scripts 下新增 OpenHarmony 的依赖:

{
  "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"
  }
}

2.2 在 AwesomeProject 目录下运行安装依赖包命令:

npm i @react-native-oh/react-native-harmony@x.x.x

这里前面已经安装完成了

运行指令并生成bundle

打开 AwsomeProject\metro.config.js,并添加 OpenHarmony 的适配代码。

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);

在 AwesomeProject 目录下运行生成 bundle 文件的命令。运行成功后,会在 AwesomeProject/harmony/entry/src/main/resources/rawfile 目录下生成 bundle.harmony.js 和 assets 文件夹,assets 用来存放图片(如果 bundle 中不涉及本地图片,则没有 assets 文件夹)。

执行命令:

npm run dev

这里我出现了报错:

这个错误是因为你的项目配置中(大概率是 metro.config.js 或 package.json)引用了 @react-native-oh/react-native-harmony/metro.config 模块,但这个模块要么没安装,要么安装路径不对—— 结合之前的操作,原因是:RN 版本的鸿蒙包不适配。

我的HarmonyOS版本是  6.0.1.112 

重新安装了一个版本的Node.js 

npm i @react-native-oh/react-native-harmony@0.72.90

重新执行命令:npm run dev

成功解决:

也可以使用 Metro 服务来加载 bundle 包,可以参考这篇文章GitCode - 全球开发者的开源社区,开源代码托管平台

三、创建鸿蒙工程

在DevEco Studio新建工程

在这里我新建了一个MyApplication_a

记得在 File ——Project Structure,在弹窗界面点击 Signing Configs,勾选 Support HarmonyOS 和 Automatically generate signature,然后点击 Sign In 登录华为账号,并签名

3.1 添加 React Native 配置

在DevEco Studio中 entry 目录下执行以下命令:

ohpm i @rnoh/react-native-openharmony@0.72.90

执行完成后会在工程级目录以及模块级目录下生成 oh_modules 文件夹。

3.2 集成RNOH

3.2.1 补充CPP侧代码

在F:\存放项目的路径\MyApplication_a\entry\src\main 目录下新建 cpp 文件夹。

在 cpp 目录下新增CMakeLists.txtPackageProvider.cpp文件,并将 RNOH 的适配层代码添加到编译构建中生成 librnoh_app.so

在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-***mand-line-argument -Qunused-arguments")
set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
add_***pile_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)

在PackageProvider.cpp文件添加以下代码

该文件需要满足以下要求:

  • 需要导入 RNOH/PackageProvider
  • 实现 getPackages 方法,用于创建三方库或自定义 TurboModule 或 Fabric 的 package 对象。
#include "RNOH/PackageProvider.h"
 
using namespace rnoh;
 
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {};
}

打开 MyApplicaton_a\entry\build-profile.json5,将 cpp 中的代码添加到应用工程的编译构建任务中

build-profile.json5文件内容如下:

{
  "apiType": "stageMode",
  "buildOption": {
    "externalNativeOptions": {
      "path": "./src/main/cpp/CMakeLists.txt",
      "arguments": "",
      "cppFlags": "",
      "abiFilters": ["arm64-v8a", "x86_64"]
    },
    "resOptions": {
      "copyCodeResource": {
        "enable": false
      }
    }
  },
  "buildOptionSet": [
    {
      "name": "release",
      "arkOptions": {
        "obfuscation": {
          "ruleOptions": {
            "enable": false,
            "files": [
              "./obfuscation-rules.txt"
            ]
          }
        }
      }
    },
  ],
  "targets": [
    {
      "name": "default"
    },
    {
      "name": "ohosTest",
    }
  ]
}

3.2.2 补充ArkTS侧的代码

3.2.2.1 修改EntryAbility.ets文件

打开 MyApplicaton_a\entry\src\main\ets\entryability\EntryAbility.ets,引入并使用 RNAbility

该文件需要满足:重写 getPagePath,返回程序的入口 page。

文件内容如下:(添加的内容后面报错了,这里记录一下,添加的内容在下面

import { RNAbility } from '@rnoh/react-native-openharmony';
import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';


const DOMAIN = 0x0000;

export default class EntryAbility extends RNAbility {
  getPagePath() {
    return 'pages/Index';
  }
}


export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    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 {
    // Main window is created, set main page for this ability
    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', 'Su***eeded in loading the content.');
    });
  }

  onWindowStageDestroy(): void {
    // Main window is destroyed, release UI related resources
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground(): void {
    // Ability has brought to foreground
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
  }

  onBackground(): void {
    // Ability has back to background
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
  }
}

这里遇到报错:

报错的内容大概为:

  1. 修改后的 CMakeLists.txt 没被正确读取(可能是没保存,或路径配置还是错的);
  2. CMake 语法顺序反了;
  3. 始终找不到 RNOH 的 cpp 目录

但是检查了一下之前配置的文件,没发现什么问题

后来看了这篇文章,发现是之前安装的版本不适配

开源鸿蒙-React编译开发HarmonyOS_开源鸿蒙 csdn-CSDN博客

把添加的内容换成:

import { RNAbility } from '@rnoh/react-native-openharmony';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
 
const DOMAIN = 0x0000;
 
export default class EntryAbility extends RNAbility {
  getPagePath() {
    return 'pages/Index';
  }
 
  override onCreate(want: Want): void {
    super.onCreate(want);
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'EntryAbility onCreate');
  }
 
  override onDestroy(): void {
    super.onDestroy();
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'EntryAbility onDestroy');
  }
 
  override onWindowStageCreate(windowStage: window.WindowStage): void {
    super.onWindowStageCreate(windowStage);
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'EntryAbility onWindowStageCreate');
  }
 
  override onWindowStageDestroy(): void {
    super.onWindowStageDestroy();
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'EntryAbility onWindowStageDestroy');
  }
 
  override onForeground(): void {
    super.onForeground();
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'EntryAbility onForeground');
  }
 
  override onBackground(): void {
    super.onBackground();
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'EntryAbility onBackground');
  }
}

成功解决报错

3.2.2.2 新增 RNPackagesFactory.ets文件

在 MyApplicaton\entry\src\main\ets 目录下新增 RNPackagesFactory.ets,该文件需要满足以下要求:

  • 在 @rnoh/react-native-openharmony 导入 RNPackageContext 和 RNPackage
  • 在文件中导出 createRNPackages 方法,用于创建三方库或自定义 TurboModule、Fabric的package 对象。

添加内容如下:

import { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [];
}

3.2.2.3 Index.ets文件添加RNOH的使用代码

打开 MyApplicaton\entry\src\main\ets\pages\Index.ets,添加RNOH的使用代码,修改后如下:

import {
  AnyJSBundleProvider,
  ***ponentBuilderContext,
  FileJSBundleProvider,
  MetroJSBundleProvider,
  ResourceJSBundleProvider,
  RNApp,
  RNOHErrorDialog,
  RNOHLogger,
  TraceJSBundleProviderDecorator,
  RNOHCoreContext
} from '@rnoh/react-native-openharmony';
import { createRNPackages } from '../RNPackagesFactory';

@Builder
export function buildCustomRN***ponent(ctx: ***ponentBuilderContext) {}

const wrappedCustomRN***ponentBuilder = wrapBuilder(buildCustomRN***ponent)

@Entry
@***ponent
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 ***pletely
    // 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
            arkTs***ponentNames: []
          },
          initialProps: { "foo": "bar" } as Record<string, string>,
          appKey: "AwesomeProject",
          wrappedCustomRN***ponentBuilder: wrappedCustomRN***ponentBuilder,
          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/***.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%')
  }
}

3.3 加载bundle包

在上一章节中已经完成了 bundle 文件的生成,接下来将它加载到 DevEco Studio 中以运行 MyApplication 项目。加载 bundle 有三种方式:

  • 方式一:本地加载 bundle。将 bundle 文件和 assets 图片放在 entry/src/main/resources/rawfile 路径下,在 entry/src/main/ets/pages/Index.ets 中使用。

  • 方式二:使用 Metro 服务加载 bundle。详细流程参考Metro热加载。

  • 方式三:加载沙箱目录的bundle:

    • 应用沙箱是一种以安全防护为目的的隔离机制,避免数据受到恶意路径穿越访问。在这种沙箱的保护机制下,应用可见的目录范围即为“应用沙箱目录”。

    • 开发者在应用开发调试时,需要向应用沙箱下推送一些文件以期望在应用内访问或测试,此时有两种方式:

      − 第一种:可以通过 DevEco Studio 向应用安装路径中放入目标文件,详见应用安装资源访问。

      − 第二种:在具备设备环境时,可以使用另一种更为灵活的方式,通过 hdc 工具来向设备中应用沙箱路径推送文件。推送命令如下,其中,沙箱路径可通过向应用沙箱推送文件查询:

      hdc file send ${待推送文件的本地路径} ${沙箱路径}

    • 加载沙箱目录 bundle,需要在 RNApp 的 jsBundlePrivider 参数中使用 new FileJSBundleProvider('bundlePath') 将 bundle 注册进框架,并运行 bundle。

在上一章节的 MyApplicaton\entry\src\main\ets\pages\Index.ets 文件中,创建 RNApp 时传入 jsBundleProvider 用于加载 bundle。jsBundleProvider 的 AnyJSBundleProvider 传入了 FileJSBundleProvider,用于沙箱目录加载 bundle

此内容来源于:GitCode - 全球开发者的开源社区,开源代码托管平台

这里我选择方法一:

把路径AwesomeProject\harmony\entry\src\main\resources\rawfile中的

assets文件和bundle.harmony.js文件复制到之前创建的项目MyApplication_a文件中的

MyApplication_a\entry\src\main\resources\rawfile

这里出现了css的样式报错:

问题出在font属性的写法上

更改

font-size: 14px;
    line-height: 14px;
    font-family: -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;

四、启动并运行工程

成功运行

转载请说明出处内容投诉
CSS教程网 » 鸿蒙 React Native 编译开发

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买