Arkts-NAPI-Rust-Demo icon indicating copy to clipboard operation
Arkts-NAPI-Rust-Demo copied to clipboard

使 Rust 开发华为鸿蒙 ArkTs N-API 原生模块的简单例程

【例程】Rust编程开发ArkTs NAPI原生模块

在该完整例程中,

  • 既包含了DevEco StudioEmpty Ability工程
  • 也包括了Cargo (Library) Package工程
  • 更涵盖了ohos-node-bindgen的用例代码。
  • 最后,Cargo (Library) Package工程根目录下的build.rspost_build.rs构建程序也能直接在其它同类Cargo Package工程内复用。

如何使普通的Empty Ability工程支持Rust原生模块开发

  1. 新建/打开DevEco Studio工程。

  2. 修改模块级build-profile.json5文件。比如,entry/build-profile.json5文件。在buildOption节点下,添加如下配置数组

    "externalNativeOptions": {
      "abiFilters": [
        "arm64-v8a",
        "armeabi-v7a",
        "x86_64"
      ]
    }
    
  3. 在模块根目录下,创建如下三个目录

    目录 CPU架构 设备类型
    模块根目录/libs/arm64-v8a 64ARM CPU 真机
    模块根目录/libs/armeabi-v7a 32ARM CPU 真机
    模块根目录/libs/x86_64 64AMD / Intel CPU 模拟器
  4. 模块根目录/src/main/目录下,创建rust文件夹。

  5. 模块根目录/src/main/rust文件夹内,使用cargo init --lib --name=<包名>命令初始Cargo Package工程

    • 【注意】交叉编译输出链接库的ABI格式不是cdylib,而是dylib。在Cargo.toml中,该设置值有些反直觉

      [lib]
      crate-type = ["dylib"]
      
    • DevEco Studio工程的新目录结构变为

      DevEco Studio 工程根目录
      ├── entry — 模块根目录
      │   ├── libs — 交叉编译输出的 *.so 文件都被复制到下面的子文件夹内
      │   │   ├── arm64-v8a
      │   │   ├── armeabi-v7a
      │   │   └── x86_64
      │   ├── src
      │   │   ├── main
      │   │   │  ├── resources
      │   │   │  ├── ets  — 旧有的 ArkTs 源码目录
      │   │   │  ├── rust — 新建的、专门盛放 Cargo (Lib) Package 工程的目录
      │   │   │  │   ├── Cargo.toml
      │   │   │  │   ├── src — Rust 源码目录
      │   │   │  │   ├── target
      │   │   │  │   │  ├── aarch64-unknown-linux-ohos
      │   │   │  │   │  │  └── release
      │   │   │  │   │  ├── armv7-unknown-linux-ohos
      │   │   │  │   │  │  └── release
      │   │   │  │   │  ├── x86_64-unknown-linux-ohos
      │   │   │  │   │  │  └── release
      
  6. 依赖ohos-node-bindgen crate基建,开发【鸿蒙ArkTs N-API】原生模块。因为由ohos-node-bindgen crate间接依赖的socket2 crate不兼容【华为-鸿蒙】操作系统,所以,需要

    1. DevEco Studio工程的平级目录,克隆stuartZhang/socket2至本地,

      git clone [email protected]:stuartZhang/socket2.git
      
    2. 将其切分支至v0.4.x

      cd socket2
      git checkout -q v0.4.x
      
    3. Cargo.toml中,局部地重写Override依赖图

      [dependencies]
      socket2 = "0.4.10"
      
      [patch.crates-io]
      socket2 = { path = "../../../../../socket2" }
      
  7. src目录下,编写Rust业务逻辑处理程序。

  8. 安装cargo-post工具链增补项,以使用cargo build命令支持【后置处理】程序。

    cargo install cargo-post
    
  9. 编写build.rspost_build.rs构建程序,将交叉编译输出的*.so文件分别复制到模块根目录/libs/arm64-v8a模块根目录/libs/armeabi-v7a模块根目录/libs/x86_64文件夹内。其中,

    • build.rs作为编译【前置处理】程序
      • 收集*.so文件的位置信息
      • 生成【文件复制】指令
      • 将【文件复制】指令追加写入到指定的*.cmd / *.sh文件内。
    • post_build.rs作为编译【后置处理】程序
      • 执行【文件复制】脚本程序文件
      • 删除该脚本程序文件

    看图吧,一图抵千词

    image

  10. 执行交叉编译指令

    cargo +nightly post build --release -Zbuild-std \
        --target=aarch64-unknown-linux-ohos \
        --target=armv7-unknown-linux-ohos \
        --target=x86_64-unknown-linux-ohos
    
  11. 交叉编译输出的【链接库】文件名被自动命名为“lib<包名>.so”。所以,若Cargo.toml定义[package] namecalculator,那么交叉编译输出的链接库文件名就是libcalculator.so

  12. ArkTs代码中,直接以【链接库】文件名为【ES Module模块名】导入原生模块,并执行它的成员方法。

    import calculator from 'libcalculator.so';
    const result = calculator.add(2, 3);
    

总得来讲,除了Rust + N-API编程门槛着实有点高之外,剩余的工作就不难了!