HarmonyOS开发实践-鸿蒙napi开发实践

鸿蒙napi开发实践

前言

HarmonyOS Node-API是基于Node.js 18.x LTS的Node-API规范扩展开发的机制,为开发者提供了ArkTS/JS与C/C++模块之间的交互能力。它提供了一组稳定的、跨平台的API,可以在不同的操作系统上使用。

一般情况下HarmonyOS应用开发使用ArkTS/JS语言,但部分场景由于性能、效率等要求,比如游戏、物理模拟等,需要依赖使用现有的C/C++库。Node-API规范封装了I/O、CPU密集型、OS底层等能力并对外暴露C接口,使用C/C++模块的注册机制,向ArkTS/JS对象上挂载属性和方法的方式来实现ArkTS/JS和C/C++的交互。主要场景如下:

  • 系统可以将框架层丰富的模块功能通过Node-API的模块注册机制对外暴露ArkTS/JS的接口,将C/C++的能力开放给应用的ArkTS/JS层。

  • 应用开发者也可以选择将一些对性能、底层系统调用有要求的核心功能用C/C++封装实现,再通过ArkTS/JS接口使用,提高应用本身的执行效率。

开发步骤
第一步:创建Native C++工程
  • 在DevEco Studio中New > Create Project,选择Native C++模板,点击Next,选择API版本,设置好工程名称,点击Finish,创建得到新工程。、

在这里插入图片描述

  • 创建工程后工程结构可以分两部分,cpp部分和ets部分

    目录结构如下:
    在这里插入图片描述

    entry:应用模块,编译构建生成一个HAP。

  • src > main > cpp > types:用于存放C++的API接口描述文件

  • src > main > cpp > types > libentry > index.d.ts:描述C++ API接口行为,如接口名、入参、返回参数等。

  • src > main > cpp > types > libentry> oh-package.json5:配置.so三方包声明文件的入口及包名。

  • src > main > cpp > CMakeLists.txt:CMake配置文件,提供CMake构建脚本。

  • src > main > cpp > napi_init.cpp:定义C++ API接口的文件

  • **src > main > ets:**用于存放ArkTS源码。

native侧的实现

设置模块注册信息

ArkTS侧import native模块时,会加载其对应的so。加载so时,首先会调用napi_module_register方法,将模块注册到系统中,并调用模块初始化函数。

napi_module有两个关键属性:一个是.nm_register_func,定义模块初始化函数;另一个是.nm_modname,定义模块的名称,也就是ArkTS侧引入的so库的名称,模块系统会根据此名称来区分不同的so。

我们对napi_init.cpp文件的代码进行解析

#include "napi/native_api.h"

// 定义 N-API 函数 Add:接收两个数字参数,返回它们的和(N-API 标准静态函数声明)
// env:N-API 环境句柄(用于访问所有 N-API 函数)
// info:回调信息句柄(包含函数调用时的参数、this 指针等上下文)
static napi_value Add(napi_env env, napi_callback_info info)
{
    // 1. 初始化参数相关变量
    size_t argc = 2;                  // 期望接收的参数个数(此处为 2 个数字)
    napi_value args[2] = {nullptr};   // 存储传入参数的数组(napi_value 是 N-API 中所有值的通用类型)

    // 2. 获取函数调用时的实际参数
    // 作用:从 info 中提取参数到 args 数组,同时更新实际接收的参数个数到 argc
    // 参数说明:env(环境句柄)、info(回调信息)、&argc(实际参数个数输出)、args(参数存储数组)、
    //          nullptr(不获取 this 指针)、nullptr(不获取额外数据)
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    // 3. (可选,此处仅获取类型未做校验)获取两个参数的数据类型
    napi_valuetype valuetype0;  // 存储第一个参数的数据类型(如数字、字符串等)
    napi_typeof(env, args[0], &valuetype0);  // 获取 args[0] 的数据类型

    napi_valuetype valuetype1;  // 存储第二个参数的数据类型
    napi_typeof(env, args[1], &valuetype1);  // 获取 args[1] 的数据类型

    // 4. 将 N-API 数值(napi_value)转换为 C 语言的 double 类型
    double value0;  // 存储第一个参数的 double 值
    // 作用:将 args[0](N-API 数值)解析为 double 并写入 value0
    napi_get_value_double(env, args[0], &value0);

    double value1;  // 存储第二个参数的 double 值
    napi_get_value_double(env, args[1], &value1);  // 解析 args[1] 为 double

    // 5. 计算总和并转换为 N-API 数值类型
    napi_value sum;  // 存储结果的 N-API 数值
    // 作用:创建一个新的 N-API 双精度数值(value0 + value1),并写入 sum
    napi_create_double(env, value0 + value1, &sum);

    // 6. 返回计算结果(N-API 函数必须返回 napi_value 类型)
    return sum;
}
// 模块初始化
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END
// 准备模块加载相关信息,将上述Init函数与本模块名等信息记录下来。
static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};
// 加载so时,该函数会自动被调用,将上述demoModule模块注册到系统中
extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&demoModule);
}

我们在napi_init.cpp中定义了一个add的方法,然后我们需要在Index.d.ts中暴露出去

export const add: (a: number, b: number) => number;
ArkTS中调用
import { hilog } from '@kit.PerformanceAnalysisKit';
//引入napi的so库
import testNapi1 from 'libentry.so';

const DOMAIN = 0x0000;

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize($r('app.float.page_text_font_size'))
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            this.message = 'Welcome';
            //调用即可
            hilog.info(DOMAIN, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi1.add(2, 3));
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

其中在entry模块中引用so库,是在oh-package.json5中配置napi的依赖路径,配置代码如下

{
  "name": "entry",
  "version": "1.0.0",
  "description": "Please describe the basic information.",
  "main": "",
  "author": "",
  "license": "",
  "dependencies": {
    "libentry.so": "file:./src/main/cpp/types/libentry"
  }
}

手动配置时,需要把鼠标放到路径下,ohpm install 一下,安装一下依赖

至此,我们就可以分两部分,napi和ArkTS各自开发各自的功能

华为开发者学堂

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏文强

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值