# 开发环境搭建

- 环境搭建

  1. 安装 DevEco Studio，详情请参考[下载](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-software-download-V5)和[安装软件](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-software-install-V5)。

  2. 设置 DevEco Studio 开发环境，DevEco Studio 开发环境需要依赖于网络环境，需要连接上网络才能确保工具的正常使用，详情请参考[配置开发环境](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-environment-config-V5)。

  3. 开发者可以参考以下链接，完成设备调试的相关配置：

      - [使用真机进行调试](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-run-device-0000001670539800-V5)
      - [使用模拟器进行调试](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-run-emulator-0000001582636200-V5)

  4. hdc环境配置

      hdc 是 OpenHarmony 为开发人员提供的用于调试的命令行工具，鸿蒙 React Native 工程使用 hdc 进行真机调试。hdc 工具通过 OpenHarmony SDK 获取，存放于 SDK 的 `toolchains` 目录下，请将 `{DevEco Studio安装路径}/sdk/{SDK版本}/openharmony/toolchains` 的完整路径添加到环境变量中。

      - windows 环境变量设置方法：

        a. 在**此电脑 > 属性 > 高级系统设置 > 高级 > 环境变量**中，编辑系统变量path，添加hdc工具路径。

        b. 在**此电脑 > 属性 > 高级系统设置 > 高级 > 环境变量**中，添加 HDC 端口变量名为：`HDC_SERVER_PORT`，变量值可设置为任意未被占用的端口，如 `7035`。

        ![环境配置-hdc](./figures/环境配置-hdc.png)
      - macOS 环境变量设置方法：

        a. 打开终端，执行以下命令，打开 `.bash_profile` 文件。

          ```bash
          vi ~/.bash_profile
          ```

          输入以下内容，在 PATH 路径下添加 HDC 工具路径和添加 `HDC_SERVER_PORT` 端口信息：

          ```bash
          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
          ```

          > HDC 端口变量名为：`HDC_SERVER_PORT`，变量值可设置为任意未被占用的端口，如 `7035`。

        b. 编辑完成后，单击 Esc 键退出编辑模式，然后输入 “wq”，单击 enter 键保存。

        c. 执行以下命令使配置的环境变量生效：

          ```bash
          source ~/.bash_profile
          ```

  5. <span id="set_capi_path">配置 CAPI 版本环境变量</span>

      当前RN框架提供的 Demo 工程默认为 CAPI 版本，您需要配置环境变量 `RNOH_C_API_ARCH = 1`。

      - Windows 环境：

        在**此电脑 > 属性 > 高级系统设置 > 高级 > 环境变量**中，在系统变量中点击新建，添加变量名为：`RNOH_C_API_ARCH`，变量值为 `1`。

        ![image](./figures/环境配置-C-API.png)

      - Mac环境：

        a. 打开终端应用程序（Terminal），您可以在"应用程序"文件夹的"实用工具"文件夹中找到它。在终端中，输入以下命令来设置环境变量：

          ```bash
          export RNOH_C_API_ARCH=1
          ```

          这将创建一个名为 `RNOH_C_API_ARCH` 的环境变量，并将其设置为 `1`。

        b. 确认环境变量是否成功设置。在终端中输入以下命令：

          ```bash
          echo $RNOH_C_API_ARCH
          ```

          如果输出为 1，则表示环境变量已成功设置。

        c. 如果您希望在每次打开终端时都自动设置该环境变量，可以将上述 export 命令添加到你的 bash 配置文件（例如~/.bash_profile、~/.bashrc 或 ~/.zshrc）。编辑相应的文件，并在末尾添加以下行：

          ```bash
          export RNOH_C_API_ARCH=1
          ```

        d. 保存文件并关闭编辑器，重新打开终端，检查环境变量是否仍然设置为 1：

          ```bash
          echo $RNOH_C_API_ARCH
          ```

          如果输出为 1，则表示环境变量已成功设置。

  6. 使用约束

      如果您需要自定义 `CMakeLists.txt`，请将 so 命名为 `rnoh_app`。

      ```CMAKE
      add_library(rnoh_app SHARED
          ···
          "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
      )
      ```

  7. 补充说明

      完成以上环境配置即可进行鸿蒙的 React Native 项目开发，安卓和 iOS 的 React Native 完整环境搭建请参考 React Native 官网[环境搭建](https://reactnative.cn/docs/environment-setup)。

# 创建React Native工程

​ 本章节主要介绍 React Native 工程的初始化，安装鸿蒙依赖包，并成功运行的过程。

## 创建新项目

​可选择一个目录，例如 D 盘根目录，使用 React Native 内置的命令行工具来创建一个名为 “AwesomeProject” 的新项目。这个命令行工具不需要安装，可以直接用 node 自带的 npx 命令来创建，目前 React Native for OpenHarmony 仅支持 **0.72.5** 版本的 React Native：

  ```bash
  npx react-native@0.72.5 init AwesomeProject --version 0.72.5
  ```

![环境搭建-AwesomeProject](./figures/环境搭建-AwesomeProject.png)

​该命令在 mac 环境下初始化 React Native 项目时会下载 iOS 依赖库，耗时较长，开发者可以选择执行以下命令跳过该过程，后续根据需要自行下载，不影响鸿蒙项目开发：

  ```bash
  npx react-native@0.72.5 init AwesomeProject --version 0.72.5 --skip-install
  ```

![环境搭建-mac-AwesomeProject](./figures/环境搭建-mac-AwesomeProject.png)

## 安装鸿蒙依赖包并生成bundle

本节中使用的各类文件的版本版本配套关系，可以参考[React Native鸿蒙化版本信息](版本说明.md)。
> 在本节中，您可以按照本节中的操作一步一步完成环境的搭建，也可以使用附件中的 `AwesomeProjectReplace.zip`，一一替换对应的文件，修改版本信息并运行。  
> 注：如果要使用Codegen，请在此处配置Codegen相关命令，详细请参考[Codegen](./Codegen.md)。

### 下载并安装鸿蒙化依赖

1. 打开 `AwesomeProject` 目录下的 `package.json`，在 `scripts` 下新增 OpenHarmony 的依赖：
   ```diff
   {
    "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. 在 `AwesomeProject` 目录下运行安装依赖包命令：
  
    ```bash
    npm i @ohmi/react-native-harmony@x.x.x
    ```
    > 注意：
    > 1. 指令中@x.x.x用于指定下载的版本，不指定时默认下载最新版本。 
    > 2. 本地安装鸿蒙化依赖,请参考[如何使用本地安装鸿蒙化依赖](./版本升级指导.md/#如何使用本地安装鸿蒙化依赖)。
    > 3. 如何从本地依赖切换至远程依赖,请参考[如何从本地依赖切换至远程依赖](./版本升级指导.md/#如何从本地依赖切换至远程依赖)。   
    ![环境搭建-依赖下载](./figures/环境搭建-依赖下载.png)  

### 运行指令并生成bundle

1. 打开 `AwsomeProject\metro.config.js`，并添加 OpenHarmony 的适配代码。配置文件的详细介绍，可以参考[React Native 中文网](https://reactnative.cn/docs/metro)。修改完成后的文件内容如下：

    ```javascript
    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);
    ```
2. 在 AwesomeProject 目录下运行生成 bundle 文件的命令。运行成功后，会在 `AwesomeProject/harmony/entry/src/main/resources/rawfile` 目录下生成 `bundle.harmony.js` 和 `assets` 文件夹，`assets` 用来存放图片（如果 `bundle` 中不涉及本地图片，则没有 `assets` 文件夹）。
  
    ```bash
    npm run dev
    ```

    ![环境搭建-生成bundle](./figures/环境搭建-生成bundle.png)

    > 如果运行时报错 “`'react-native'不是内部或外部命令，也不是可运行的程序或批处理文件。`”，请重新运行 `npm install` 命令。

3. 您也可以使用 Metro 服务来加载 bundle 包。具体使用方式，可以参考[Metro热加载](调试调测.md#metro热加载)。

# 创建鸿蒙工程

本章节主要介绍了鸿蒙工程的创建，并加载 React Native 相关的依赖包和 bundle 包，最终完成鸿蒙工程的运行的过程。
> 您可以按照本节中的操作一步一步完成环境的搭建，也可以使用附件中的 `MyApplicationReplace.zip` 的内容，一一替换对应的文件，修改版本并运行。

## 新建工程或者在已有工程中集成

​ 下面演示在新建工程中集成。点击 `File > New > Create Project`，选择创建 `Empty Ability` 工程，如图所示：

![环境搭建-EmptyAbility](./figures/环境搭建-EmptyAbility.png)

​ 点击 `Next` 按钮，并在 `Compile SDK` 中选择 `API13`，创建一个名为 “MyApplication” 的项目。注意项目路径不要太长，如图所示：

![环境搭建-version](./figures/环境搭建-version.png)

​ 连接真机，点击 `File > Project Structure`，在弹窗界面点击 `Signing Configs`，勾选 `Support OpenHarmony` 和 `Automatically generate signature`，然后点击 `Sign In` 登录华为账号，并签名。

![环境搭建-signing](./figures/环境搭建-signing.png)

## 添加 React Native 配置

在 “MyApplication” 的项目中运行

    npm i @ohmi/react-native-openharmony@x.x.x

在 `entry` 目录下执行以下命令：

    ohpm i @rnoh/react-native-openharmony@"file:../node_modules/@ohmi/react-native-openharmony/react_native_openharmony.har"

执行完成后会在工程级目录以及模块级目录下生成 `oh_modules` 文件夹。
> 注意：
> 1. 指令中@x.x.x用于指定下载的版本，不指定时默认下载最新版本。 
> 2. 由于 har 包比较大，这一步耗时会比较长，务必保证 `ohpm install` 和 IDE 自发的 `SyncData` 全都完成，否则会导致编译报错。
> 3. 如何在三方库或自定义module中引入rnoh的依赖，请参考[文档](./常见开发场景.md/#如何在三方库或自定义module中引入rnoh的依赖)。

## 在原生工程中集成RNOH

### 补充CPP侧代码

1. 在 `MyApplication/entry/src/main` 目录下新建 cpp 文件夹。
2. 在 cpp 目录下新增 `CMakeLists.txt`，并将 **RNOH** 的适配层代码添加到编译构建中生成 `librnoh_app.so`：
    ```CMAKE
    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)
    ```
3. 在 cpp 目录下新增 `PackageProvider.cpp`，该文件需要满足以下要求：
   - 需要导入 `RNOH/PackageProvider`；
   - 实现 `getPackages` 方法，用于创建三方库或自定义 TurboModule 或 Fabric 的 package 对象。
   > 此处不涉及三方库与自定义 TurboModule 或组件，需要返回空数组。
    ```cpp
    #include "RNOH/PackageProvider.h"

    using namespace rnoh;

    std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
        return {};
    }
    ```
4. 打开 `MyApplicaton\entry\build-profile.json5`，将 cpp 中的代码添加到鸿蒙的编译构建任务中，详细介绍可以参考[模块级build-profile.json5](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-hvigor-build-profile-0000001778834297-V5#section11914746114811)：
   ```diff
   {
    "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",
      }
    ]
   }
   ```

### 补充ArkTS侧的代码

1. 打开 `MyApplicaton\entry\src\main\ets\entryability\EntryAbility.ets`，引入并使用 `RNAbility`，该文件需要满足以下的要求：
   - 如果需要扩展使用对应的生命周期函数，请在代码中使用 super，`RNAbility` 在生命周期函数中进行了对应的操作，需要使用 super 保证功能不丢失；
   - 需要重写 `getPagePath`，返回程序的入口 page。
    ```typescript
    import { RNAbility } from '@rnoh/react-native-openharmony';

    export default class EntryAbility extends RNAbility {
      getPagePath() {
        return 'pages/Index';
      }
    }
    ```
2. 在 `MyApplicaton\entry\src\main\ets` 目录下新增 `RNPackagesFactory.ets`，该文件需要满足以下要求：
   - 在 `@rnoh/react-native-openharmony` 导入 `RNPackageContext` 和 `RNPackage`；
   - 在文件中导出 `createRNPackages` 方法，用于创建三方库或自定义 TurboModule、Fabric的package 对象。
    > 此处不涉及三方库与自定义TurboModule或组件，需要返回空数组。
    ```typescript
    import { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony/ts';
    export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
      return [];
    }
    ```
3. 打开`MyApplicaton\entry\src\main\ets\pages\Index.ets`，添加RNOH的使用代码，修改后如下：
   > `RNApp`的参数`appKey`需要与RN工程中`AppRegistry.registerComponent`注册的`appName`保持一致，否则会导致白屏。
    ```javascript
    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%')
      }
    }
    ```

## 加载bundle包

​ 在上一章节中已经完成了 bundle 文件的生成，接下来将它加载到 DevEco Studio 中以运行 MyApplication 项目。加载 bundle 有三种方式：
- 方式一：本地加载 bundle。将 bundle 文件和 assets 图片放在 `entry/src/main/resources/rawfile` 路径下，在 `entry/src/main/ets/pages/Index.ets` 中使用。
- 方式二：使用 Metro 服务加载 bundle。详细流程参考[Metro热加载](调试调测.md#metro热加载)。
- 方式三：加载沙箱目录的bundle：
  
  - [应用沙箱](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/file-management/app-sandbox-directory.md)是一种以安全防护为目的的隔离机制，避免数据受到恶意路径穿越访问。在这种沙箱的保护机制下，应用可见的目录范围即为“应用沙箱目录”。
  - 开发者在应用开发调试时，需要向应用沙箱下推送一些文件以期望在应用内访问或测试，此时有两种方式：

    − 第一种：可以通过 DevEco Studio 向应用安装路径中放入目标文件，详见[应用安装资源访问](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/quick-start/resource-categories-and-access.md)。

    − 第二种：在具备设备环境时，可以使用另一种更为灵活的方式，通过 hdc 工具来向设备中应用沙箱路径推送文件。推送命令如下，其中，沙箱路径可通过[向应用沙箱推送文件查询](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/file-management/send-file-to-app-sandbox.md)：

    ```bash
    hdc file send ${待推送文件的本地路径} ${沙箱路径}
    ```

  - 加载沙箱目录 bundle，需要在 RNApp 的 `jsBundlePrivider` 参数中使用 `new FileJSBundleProvider('bundlePath')` 将 bundle 注册进框架，并运行 bundle。

在 `MyApplication/entry` 目录下 `Index.ets` 文件中，创建 `RNApp` 时传入 `jsBundleProvider` 用于加载 bundle。如下所示这里传入了 `FileJSBundleProvider`，用于沙箱目录加载 `bundle`。

## 启动并运行工程

​ 使用 DevEco Studio 运行 MyApplication 工程。执行完成后，控制台如图所示：

![环境搭建-run](./figures/环境搭建-run.png)

> 全量编译 C++ 代码耗时较长，请耐心等待。

## release包使用
> 执行完'npm i @react-native-oh/react-native-harmony@x.x.x'命令后，在生成的node_modules/@react-native-harmony中即可获取release包。

1. 在 `MyApplication` 目录下新建 libs 文件夹，将 `react_native_openharmony_release-x.x.x.xxx.har` 放入该目录。

2. 打开 `MyApplication/entry` 下的 `oh-package.json5`，替换 har 包的依赖为对应版本的 release 包：
    ```diff
    {
      "name": "entry",
      "version": "1.0.0",
      "description": "Please describe the basic information.",
      "main": "",
      "author": "",
      "license": "",
      "dependencies": {
    +     "@rnoh/react-native-openharmony": "file:../libs/react_native_openharmony_release-x.x.x.xxx.har"
      }
    }
    ```

3. 替换 `MyApplication\entry\src\main\cpp\CMakeLists.txt` 文件为以下代码:

    ```CMAKE
    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/include")
    set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated")
    set(LOG_VERBOSITY_LEVEL 1)
    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")
    set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
    add_compile_definitions(WITH_HITRACE_SYSTRACE)

    include("${RNOH_CPP_DIR}/react-native-harmony.cmake")

    # RNOH_BEGIN: manual_package_linking_1
    add_subdirectory("${OH_MODULE_DIR}/@rnoh/sample-package/src/main/cpp" ./sample-package)
    # RNOH_END: manual_package_linking_1

    file(GLOB GENERATED_CPP_FILES "./generated/*.cpp")

    add_library(rnoh_app SHARED
        ${GENERATED_CPP_FILES}
        "./PackageProvider.cpp"
        "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
    )

    target_link_libraries(rnoh_app PUBLIC rnoh)

    # RNOH_BEGIN: manual_package_linking_2
    target_link_libraries(rnoh_app PUBLIC rnoh_sample_package)
    # RNOH_END: manual_package_linking_2
    ```

4. 将 `MyApplication/entry` 的 `oh_modules` 文件夹删除，点击 `entry` 文件夹，再点击顶部菜单栏的 `build`>`Clean Project` 清除项目缓存。
5. 点击顶部菜单栏的 `File` > `Sync and Refresh Project` 来执行 `ohpm install`，执行完成后会在 `entry` 目录下生成 `oh_modules` 文件夹。
6. 点击顶部菜单栏的 `Run`>`Run 'entry'` 运行工程，运行成功后截图如下：
![release包运行成功](./figures/release包运行成功.png)

## 字节码格式的release包使用

字节码格式的release包是指在release模式下编译出来、ets文件转换成了.abc、C++源码转换成.so的har包，其命名格式为 `react_native_openharmony_abc-x.x.x.xxx.har`。  
字节码格式的release包使用方式跟release包使用大体上是一样的，主要有两点需要注意：

1. 使用字节码格式的release包需要将工程级的 build-profile.json5 文件中将 useNormalizedOHMUrl 设置为 true，如：

    ```diff
      "products": [
        {
          "name": "default",
          "signingConfig": "default",
          "compatibleSdkVersion": "5.0.1(13)",
          "runtimeOS": "HarmonyOS",
    +     "buildOption": {
    +       "strictMode": {
    +          "useNormalizedOHMUrl": true
    +       }
    +     }
        }
      ],
    ```

2. Codegen 生成的ets文件不能放到 `oh_modules/@rnoh/react-native-openharmony` 目录下。如遇这种情况，需将来 `codegen-harmony` 命令的 `--rnoh-module-path` 参数换成 `--ets-output-path`，并将路径指定到源码中（如：`entry/src/main/ets`），同时修改源码中对应文件的导入路径，如：

    ```diff
    - "codegen": "react-native codegen-harmony --cpp-output-path ../NativeProject/entry/src/main/cpp/generated --rnoh-module-path ../NativeProject/entry/oh_modules/@rnoh/react-native-openharmony --no-safety-check"
    + "codegen": "react-native codegen-harmony --cpp-output-path ../NativeProject/entry/src/main/cpp/generated --ets-output-path ../NativeProject/entry/src/main/ets --no-safety-check"
    ```

    ```diff
    - import { RNC } from "@rnoh/react-native-openharmony/generated/ts"
    + import { RNC } from "./generated/ts"
    ```