如何在原生鸿蒙APP中使用RN的bundle包

一、创作背景

上一篇博客中,我给大家分享了如何创建一个RN的项目,并且解决了其中的问题点,成功打出了Bundle包。接下来就是我给大家分享一下,如何在原生鸿蒙项目中使用那个Bundle包,这一篇分享完才算是开发环境真正的搭建好了。

在本篇中,我将继续分享环境搭建中会遇到的坑点,帮助大家快速搭建成功。

二、搭建鸿蒙原生RN的原生项目部分

1、创建一个普通的鸿蒙原生项目

2、复制鸿蒙tgz包到项目中

在RN项目最外层新建2个文件夹,分别为react-native-harmony以及react-native-harmony-cli。然后将鸿蒙的相应这2个tgz包分别放入相应文件夹中,目录层级大概是这样子:

3、关联鸿蒙tgz文件

打开package.json,在dependencies中关联上前面新建的tgz包

4、关联openharmony包

在鸿蒙项目的根目录中,新建一个libs文件夹。

将官方提供的openharmony包复制进去,如下图。

接下来修改一下原生项目的oh-package.json文件,关联一下刚刚复制进去的openharmony包。

5、安装鸿蒙所需的依赖

打开命令行,进入原生项目的根目录,运行ohpm install命令。

如果报下面的错误,说明上一步,关联包有问题,找不到包:

如果安装依赖成功,命令行会是下面这样子:

此时进入鸿蒙原生项目中查看,会发现多了ohpm_module的文件夹:

6、补充C++侧代码

  1. MyApplication/entry/src/main 目录下新建 cpp 文件夹。

  2. 在 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)
    
  3. 在 cpp 目录下新增 PackageProvider.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,将以下代码直接复制进去即可:

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

7、补充arkts侧代码

  1. 打开 MyApplicaton\entry\src\main\ets\entryability\EntryAbility.ets,之前默认是继承自UIAbility,这里要改成继承RNAbility。并且需要重写 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,把以下代码复制进去即可:

    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的使用代码,修改后如下:

    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%')
    }
    }

8、复制RN生成的业务bundle到项目中

将之前RN那边生成的bundle 文件和 assets 图片放在 entry/src/main/resources/rawfile 路径下,由于上一步我们在代码中设置了从rawfile下面获取,所以就能加载到这个目录。

9、重新编译项目依赖

上面已经把鸿蒙的RN相关依赖都配置好了,接下来进入DevEco Studio,选择File->Sync and Refresh Project选项,编译器会把前面设置的依赖都加载进编译器的内存。

10、构建和运行项目

点击运行按钮即可。接下来就是摘取胜利果实的时候了,也是最容易出现问题的时候。我会将其中可能出现的问题,列举出来分享出来给大家。

问题一 没有配置cmakelist

如果你没有配置cmakelist或者配置不正确,将会报如下错误:

解决方案:详见步骤6,新建cpp文件,将配置代码复制进入即可。

问题二 项目路径太长

如果你的项目路径名字太长,获取文件夹放得太深,导致整个路径超过260个字符,也会编译不过,具体报错信息如下:

解决方案:将项目路径放浅一些,然后路径里每个文件夹名字都改简单一些。

问题三 Ability继承错误

你想使用RN的那个Ability需要继承自RNAbility,如果你忘记改了,继续继承UIAbility的话,那么就会报一下错误:

解决方案:参考步骤7,修改继承的父类即可。

问题四 鸿蒙依赖错误

如果你的cpp环境没有配置好,或者是没有把包复制到前面说过的目录下,就会报这个错误:

解决方案:参考步骤6,配置好cpp相关依赖。

问题五 官方DEMO方法名错误

如果你是直接下载的官方DEMO,运行的时候会报这个方法找不到的错误。

解决方案:这个是官方DEMO自带的问题,把报错的地方instance->callFunction改成instance->callJSFunction即可。

三、本文总结

在本文中,我分享了如何搭建鸿蒙原生这边的环境,可以让RN那边打出来的bundle包跑起来。同时列出来了很多大家都很容易碰到的问题,并且给出了对应解决方案,希望能对大家有所帮助。

接下来,我将分享RN在鸿蒙端的热加载和调试配置,以及分享其中的踩坑点,感兴趣的家人们可以点赞关注一下。文中有表达错误的地方,也欢迎大家批评指正,共同进步。

相关推荐
️ 邪神1 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】自定义View
flutter·ios·鸿蒙·reactnative·anroid
鸿蒙自习室13 小时前
鸿蒙多线程开发——线程间数据通信对象02
ui·harmonyos·鸿蒙
SuperHeroWu716 小时前
【HarmonyOS】鸿蒙应用接入微博分享
华为·harmonyos·鸿蒙·微博·微博分享·微博sdk集成·sdk集成
郝晨妤2 天前
鸿蒙原生应用开发元服务 元服务是什么?和App的关系?(保姆级步骤)
android·ios·华为od·华为·华为云·harmonyos·鸿蒙
Peace*2 天前
HarmonyOs鸿蒙开发实战(16)=>沉浸式效果第一种方案一窗口全屏布局方案
harmonyos·鸿蒙·鸿蒙系统
遇到困难睡大觉哈哈2 天前
ArkTS组件结构和状态管理
鸿蒙
ChinaDragonDreamer2 天前
HarmonyOS:UIAbility组件间交互(设备内)
开发语言·harmonyos·鸿蒙
ChinaDragonDreamer3 天前
HarmonyOS:使用常用组件构建页面
开发语言·harmonyos·鸿蒙
鸿蒙自习室4 天前
鸿蒙动画开发07——粒子动画
ui·华为·harmonyos·鸿蒙
少恭写代码4 天前
Taro首个支持鸿蒙的 UI 库,同时还兼容 React Native、小程序、H5
react native·鸿蒙·taro·duxapp