鸿蒙rn项目搭建运行

684 阅读5分钟

鸿蒙rn项目搭建运行

先甩官方文档地址:gitee.com/openharmony…

步骤:

  1. 搭建一个rn项目
    npx react-native@0.72.5 init AwesomeProject --version 0.72.5
    
  2. 安装鸿蒙依赖,将项目打包为bundle包
    // 1.在项目的package.json文件的scripts里面添加运行命令
    scripts:{
        ...
        "dev": "react-native bundle-harmony --dev"
        ...
    }
    // 2. 安装鸿蒙依赖
    npm i @react-native-oh/react-native-harmony@x.x.x
    // 3. 运行npm run dev 根目录会多出一个harmony文件夹
    harmony\entry\src\main\resources\rawfile 问价夹下会有打包好的bundle文件和assets资源目录
    
  3. 搭建鸿蒙工程(没啥特别的)
  4. 添加rn配置(在entry下打开终端运行)
    ohpm i @rnoh/react-native-openharmony@x.x.x
    
  5. 补充CPP侧代码
  6. 在 MyApplication/entry/src/main 目录下新建 cpp 文件夹。
  7. 在 cpp 目录下新增 CMakeLists.txt,并将 RNOH 的适配层代码添加到编译构建中生成 librnoh_app.so
 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)
  1. 在 cpp 目录下新增 PackageProvider.cpp,该文件需要满足以下要求:

    • 需要导入 RNOH/PackageProvider

    • 实现 getPackages 方法,用于创建三方库或自定义 TurboModule 或 Fabric 的 package 对象。

    此处不涉及三方库与自定义 TurboModule 或组件,需要返回空数组。

    #include "RNOH/PackageProvider.h"
    
    using namespace rnoh;
    
    std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
        return {};
    }
    
  2. 打开 MyApplicaton\entry\build-profile.json5,将 cpp 中的代码添加到鸿蒙的编译构建任务中,详细介绍可以参考模块级build-profile.json5

    {
     "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。
    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或组件,需要返回空数组。

    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保持一致,否则会导致白屏。

    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 HarmonyOS
                  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热加载

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

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

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

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

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

      hdc file send ${待推送文件的本地路径} ${沙箱路径}
      
    • 加载沙箱目录 bundle,需要在 RNApp 的 jsBundlePrivider 参数中使用 new FileJSBundleProvider('bundlePath') 将 bundle 注册进框架,并运行 bundle。

在 MyApplication/entry 目录下 Index.ets 文件中,创建 RNApp 时传入 jsBundleProvider 用于加载 bundle。如下所示这里传入了 FileJSBundleProvider,用于沙箱目录加载 bundle

然后运行就行了运行报错的话在entry下的build-profile.json5文件下添加
 "buildOption": {
   "externalNativeOptions": {
     "path": "./src/main/cpp/CMakeLists.txt",
     "arguments": "",
     "cppFlags": "",
     + "abiFilters": [
     +   "arm64-v8a",
     +   "x86_64"
     + ]
   }
 },