用Rust开发鸿蒙应用(ArkTS NAPI)
作者:mmseoamin日期:2023-12-11

开发流程

在DevEco Studio的模板工程中包含使用Native API的默认工程,使用File->New->Create Project创建Native C++模板工程。

在此基础上进行修改

删除

entry/src/main/cpp

打开

entry/build-profile.json5

删除c++ build 配置

{
  "apiType": "stageMode",
  "buildOption": {
    // "externalNativeOptions": {
    //  "path": "./src/main/rust/CMakeLists.txt",
    //  "arguments": "",
    //  "cppFlags": "",
    //  }
  },
  "targets": [
    {
      "name": "default",
      "runtimeOS": "HarmonyOS"
    },
    {
      "name": "ohosTest",
    }
  ]
}

创建rust项目

cargo new hello

修改 Cargo.toml

[package]
name = "hello"
version = "0.1.0"
edition = "2021"
[lib]
name = "hello"
crate-type = ["dylib"]
[dependencies]
oh-napi-sys = "0.1" 
ctor = "0.1"

lib.rs 添加测试代码

use std::ffi::{CString};
use std::ptr::{null_mut};
use oh_napi_sys::*;
use ctor::ctor;
extern "C" fn add(env: napi_env, info: napi_callback_info) ->napi_value{
    // 期望从ArkTS侧获取的参数的数量,napi_value可理解为ArkTS value在Native方法中的表现形式。
    let mut args:[napi_value; 2] = [null_mut();2];
    let mut argc = args.len();
    unsafe {
        napi_get_cb_info(env, info, &mut argc, args.as_mut_ptr(), null_mut(), null_mut());
        let mut valuetype0  = napi_valuetype_napi_undefined;
        napi_typeof(env, args[0], &mut valuetype0);
        let mut valuetype1  = napi_valuetype_napi_undefined;
        napi_typeof(env, args[1], &mut valuetype1);
        if (valuetype0 != napi_valuetype_napi_number) || (valuetype1 != napi_valuetype_napi_number) {
            let mut undefined: napi_value= null_mut();
            napi_get_undefined(env, &mut undefined);
            return undefined;
        }
        // 将获取的ArkTS参数转换为Native信息,此处ArkTS侧传入了两个number,这里将其转换为Native侧可以操作的double类型。
        let mut value0 = 0f64;
        napi_get_value_double(env, args[0], &mut value0);
        let mut value1 = 0f64;
        napi_get_value_double(env, args[1], &mut value1);
        // Native侧的业务逻辑,这里简单以两数相加为例。
        let native_sum = value0 + value1;
        // 此处将Native侧业务逻辑处理结果转换为ArkTS值,并返回给ArkTS。
        let mut sum = null_mut();
        napi_create_double(env, native_sum, &mut sum);
        return sum;
    }
}
type Callback = extern "C" fn(env: napi_env, info: napi_callback_info) -> napi_value;
unsafe fn new_func_descriptor(name: &'static str, f: Callback) ->napi_property_descriptor{
    let name= CString::new(name).unwrap();
    napi_property_descriptor{
        utf8name: CString::into_raw(name),
        name: null_mut(),
        method: Some(f),
        getter: None,
        setter: None,
        value: null_mut(),
        attributes: napi_property_attributes_napi_default,
        data: null_mut(),
    }
}
// 注册 module
#[ctor]
fn register_hello_module(){
    let name= CString::new( "hello").unwrap();
    let mut hello_module = napi_module{
        nm_version: 1,
        nm_flags: 0,
        nm_filename: null_mut(),
        nm_register_func: Some(init),
        nm_modname: name.as_ptr() as _,
        nm_priv: 0 as * mut _,
        reserved: [0 as * mut _;4],
    };
    unsafe {
        napi_module_register(&mut hello_module);
    }
    // Init将在exports上挂上Add/NativeCallArkTS这些Native方法,此处的exports就是开发者import之后获取到的ArkTS对象。
    unsafe extern "C" fn init(env: napi_env , exports: napi_value )-> napi_value {
        let desc =[
            new_func_descriptor("add", add),
        ];
        let count = desc.len();
        napi_define_properties(env, exports, count, desc.as_ptr());
        return exports;
    }
}

添加对应ts代码

// entry/src/main/rust/types/libhello/index.d.ts
export const add: (a: number, b: number) => number;
// entry/src/main/rust/types/libhello/oh-package.json5
{
  "name": "libhello.so",
  "types": "./index.d.ts",
  "version": "",
  "description": "Please describe the basic information."
}

配置依赖

// entry/oh-package.json5
{
  "name": "entry",
  "version": "1.0.0",
  "description": "Please describe the basic information.",
  "main": "",
  "author": "",
  "license": "",
  "dependencies": {
    "libhello.so": "file:./src/main/rust/types/libhello"
  }
}

在 rust 根目录下编译,添加ohos目标的教程可以参考 Rust交叉编译OpenHarmony应用动态库

cargo build -Zbuild-std  --release  --target aarch64-unknown-linux-ohos

将编译好的 libhello.so 拷贝至

entry/libs/arm64-v8a

修改页面 Index.ets

import testNapi from 'libhello.so'
// onClick 里添加
hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3));

编译app点击hello world,日志中看到 Test NAPI 2 + 3 = 5