ArkTs之NAPI学习

news2025/1/6 19:15:40

1.Node-api组成架构

为了应对日常开发经的网络通信、串口访问、多媒体解码、传感器数据收集等模块,这些模块大多数是使用c++接口实现的,arkts侧如果想使用这些能力,就需要使用node-api这样一套接口去桥接c++代码。Node-api整体的架构图如下:

Node-api接口是基于node.js的一个扩展,所以在平常开发过程也可以参照node.js官网,像接口实现的功能,入参都是类似的。

Framework模块介绍

1)ModuleManager是管理对象的模块,当arkts侧调用c++时,会加载Native侧的模块到ModuleMangger,并转化为arkts对象返回上层。

2)ScopeManager:用于管理napi_value生命周期,napi_value是Node-api独特的数据类型,类似于ArkTs中的number、String等各种参数类型的统一表示形式,在Native侧代码开发中不需要感知不同类型的数据类型,统一都是napi_value。

3)ReferenceManager:用于管理引用,开发时经常会有一些跨线程的场景,这个时候就需要创建引用(napi_ref),否则就就会被GC回收掉。napi_ref用于指向napi_value,允许用户管理napi_value值的生命周期。

3)Native Engine的作用是统一ArkTS引擎在Node-API层的接口行为。

4)方舟运行时,也就是ArkTS引擎,整个Node-API模块都是跑在方舟运行时的。

2.Napi和native的交互流程

主要分为两步:模块初始化和函数调用

模块初始化

ArkTS在import一个so库的时候,会先找到ArkTS引擎,ArkTS引擎会加载模块信息到ModuleMananger,其实就是对应的dlopen函数(首次调用时加载,多次调用回去缓存查找)。之后ModuleManager会把模块信息返回给ArkTS引擎。ArkTS引擎拿到模块信息后,在native层触发模块注册,来初始化模块。注册完成之后通过底层框架返回给上层一个ArkTS对象,这个对象上挂在着c/c++侧的方法。我们拿到arkts对象后,就可以去调用c/c++的方法了。

函数调用

当arkts侧通过上述import返回的对象的调用方法时,ArkTS引擎会找到并调用对应的C/C++方法

3.Node-API支持的数据类型

napi_status:枚举数据类型,表示Node-API接口返回的状态信息。每当调用一个Node-API函数,都会返回该值,表示操作成功与否的相关信息。枚举信息如下:

typedef enum{
    napi_ok,
    napi_invalid_arg,
    napi_object_expected,
    napi_string_expected,
    napi_name_expected,
    ...
    napi_would_deadlock
} napi_status;

if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
    return nullptr;
}

Node-API将基本数据类型公开为各种API使用的抽象。这些API应该被视为不透明的,只能通过其他Node-API调用进行自省。

  • napi_value:在Native侧代码开发中不需要感知不同类型的数据,统一都是napi_value
    举例说明:napi_value napiResult; napi_create_double(env, 2.0, &napiResult);
  • napi_env:Native侧函数入参,表示Napi-API执行时的上下文。退出native侧的时,napi_env将失效
  • napi_callback_info: Native侧函数的入参,保存了ArkTS侧的参数信息,用于传递给napi_get_cb_info()函数获取ArkTS侧入参信息。
    举例说明:
    static napi_value MyHypot(napi_env env, napi_callback_info info)
    {
        size_t argc = 2;
        napi_value args[2] = {nullptr};
        // 从info中获取参数信息到参数数组args[]
        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    }

    4.Node-API接口

Node-API接口在Node.js提供的原生模块基础上,目前只支持部分接口。常用的部分接口介绍如下:

  • napi_get_cb_info: 从给定的napi_callback_info中获取有关调用的详细信息,如参数和this指针
    static napi_value MyHypot(napi_env env, napi_callback_info info)
    {
        size_t argc = 2;
        napi_value args[2] = {nullptr};
        // 从info中获取参数信息到参数数组args[]
        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    }
  • napi_get_value_double: 获取给定ArkTS的number类型
    double valueX = 0.0;
    //获取args[0]存储的参数信息并赋值给valueX,args[]中存储接受ArkTs侧参数
    napi_get_value_double(env, args[0], &valueX); 

  • napi_create_string_utf8: 通过utf8编码的c字符串数据创建ArtTS侧String类型的数据

    std::string str = "temp";
    napi_value sum; //用于存储转换类型后的string类型数据
    napi_create_string_utf8(env, str.c_str(), str.length(). &sum);

    5.案例讲解说明交互流程

目录说明

CPP目录

        types/index.d.ts:定义了c++侧需要暴漏在ArkTS侧的接口

        oh-package.json5是描述index.d.ts文件的

        napi_init.cpp文件在里面做Native模块的注册、类型的转换以及大部分业务逻辑

ets目录

page/index.ets 里面有ArkTS调用C++

src目录

build-profile.json5:主要是配置构建信息(主要是cmakelist的构建路径,构建的时候能够找到cmakelist文件)

oh-package.json5:主要配置了一些模块本身的信息和依赖的工程

6.官方代码案例


基于Node-API开发业务功能

static napi_value MyHypot(napi_env env, napi_callback_info info)
{
    if ((nullptr == env) || (nullptr == info)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot", "env or exports is null");
        return nullptr;
    }
    // Number of parameters.
    size_t argc = 2;
    // Declare parameter array.
    napi_value args[2] = { nullptr };
    // Gets the arguments passed in and puts them in the argument array.
    if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot", "api_get_cb_info failed");
        return nullptr;
    }
    // Converts arguments passed in to type double.
    double valueX = 0.0;
    double valueY = 0.0;
    if (napi_ok != napi_get_value_double(env, args[0], &valueX) ||
        napi_ok != napi_get_value_double(env, args[1], &valueY)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot", "napi_get_value_double failed");
        return nullptr;
    }
    // The hypot method of the C standard library is called to perform the calculation.
    double result = hypot(valueX, valueY);
    napi_value napiResult;
    if (napi_ok != napi_create_double(env, result, &napiResult)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot", "napi_create_double failed");
        return nullptr;
    }
    return napiResult;
}


接口映射

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    if ((nullptr == env) || (nullptr == exports)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null");
        return exports;
    }
    napi_property_descriptor desc[] = {
        { "myHypot", nullptr, MyHypot, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    if (napi_ok != napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
        return nullptr;
    }
    return exports;
}
EXTERN_C_END

模块注册

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "hello",
    .nm_priv = ((void *)0),
    .reserved = { 0 }
};
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
    napi_module_register(&demoModule);
}


模块构建配置

cmakelist.txt文件:

# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(NativeTemplateDemo)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(
    ${NATIVERENDER_ROOT_PATH}
    ${NATIVERENDER_ROOT_PATH}/include
)
find_library(
    # Sets the name of the path variable.
    hilog-lib
    # Specifies the name of the NDK library that
    # you want CMake to locate.
    hilog_ndk.z
)
add_library(hello SHARED hello.cpp)
target_link_libraries(hello PUBLIC ${hilog-lib} libace_napi.z.so libc++.a)

根目录下的build-profile.json5

{
  "apiType": 'stageMode',
  "buildOption": {
    "externalNativeOptions": {
      "path": "./src/main/cpp/CMakeLists.txt",
      "arguments": "",
      "abiFilters": [
        "arm64-v8a",
        "x86_64"
      ],
      "cppFlags": "",
    }
  },
  "targets": [
    {
      "name": "default",
      "runtimeOS": "HarmonyOS"
    }
  ]
}

导出native接口

/entry/oh-package.json5

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

7.更多接口说明参照官网

华为开发者学堂

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2271021.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue el-data-picker选中开始时间,结束时间自动加半小时

效果 思路 查阅elemnet plus官网,日期时间选择器type"datetimerange"这个选中开始时间并没有对应事件会被触发,因此思路更换成type"datetime"的两个组成一起可以通过监听开始时间v-model的值变化更新结束时间的值。 代码 日期时间…

gitlab高级功能之 CICD Steps

CICD Steps 1. 介绍2. 定义 Steps2.1 Inputs2.2 Outputs 3. Using steps3.1 Set environment variables3.2 Running steps locally 4. Scripts5. Actions5.1 已知的问题 6. 表达式7. 实操7.1 单个step7.2 多个step7.3 复用steps7.4 添加output到step7.5 使用远程step 1. 介绍 …

TVS二极管选型【EMC】

TVS器件并联在电路中,当电路正常工作时,他处于截止状态(高阻态),不影响线路正常工作,当线路处于异常过压并达到其击穿电压时,他迅速由高阻态变为低阻态,给瞬间电流提供一个低阻抗导通…

122.【C语言】数据结构之快速排序(Hoare排序的优化)

目录 1.解决方法(即优化方法) 方法1.随机选key 运行结果 方法2:三数取中 1.含义 2.做法 3.代码 1.若arr[left] < arr[mid_i],则arr[right]可能的位置也有三处 2.若arr[left] > arr[mid_i],则arr[right]可能的位置也有三处 2.证明当key_ileft时,right先走,使left…

Golang的容器编排实践

Golang的容器编排实践 一、Golang中的容器编排概述 作为一种高效的编程语言&#xff0c;其在容器编排领域也有着广泛的运用。容器编排是指利用自动化工具对容器化的应用进行部署、管理和扩展的过程&#xff0c;典型的容器编排工具包括Docker Swarm、Kubernetes等。在Golang中&a…

《Spring Framework实战》2:Spring快速入门

欢迎观看《Spring Framework实战》视频教程 Spring快速入门 目录 1. Java™开发套件&#xff08;JDK&#xff09; 2. 集成开发人员环境&#xff08;IDE&#xff09; 3. 安装Maven 4. Spring快速入门 4.1. 开始一个新的Spring Boot项目 4.2. 添加您的代码 4.3. 尝…

HTML——66.单选框

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>单选框</title></head><body><!--input元素的type属性&#xff1a;(必须要有)--> <!--单选框:&#xff08;如所住省会&#xff0c;性别选择&…

rouyi(前后端分离版本)配置

从gitee上下载&#xff0c;复制下载地址&#xff0c;到 点击Clone&#xff0c;下载完成&#xff0c; 先运行后端&#xff0c;在运行前端 运行后端&#xff1a; 1.配置数据库&#xff0c;在Navicat软件中&#xff0c;连接->mysql->名字自己起(rouyi-vue-blog),用户名roo…

基于云架构Web端的工业MES系统:赋能制造业数字化变革

基于云架构Web端的工业MES系统:赋能制造业数字化变革 在当今数字化浪潮席卷全球的背景下,制造业作为国家经济发展的重要支柱产业,正面临着前所未有的机遇与挑战。市场需求的快速变化、客户个性化定制要求的日益提高以及全球竞争的愈发激烈,都促使制造企业必须寻求更加高效、智…

如何解决电脑提示缺失kernel32.dll文件错误,kernel32.dll文件缺失、损坏或错误加载问题解决方案

电脑运行故障深度解析&#xff1a;从文件丢失到系统报错&#xff0c;全面应对kernel32.dll问题 在数字化时代&#xff0c;电脑已经成为我们日常生活和工作中不可或缺的工具。然而&#xff0c;电脑在长时间运行过程中&#xff0c;难免会遇到各种问题&#xff0c;如文件丢失、文…

leecode300.最长递增子序列

dp[i]表示以nums[i]这个数结尾的时的严格递增子序列的最长长度&#xff0c;那么只要每次增加一个数字nums[i]并且这个nums[i]比之前的nums[j]要大&#xff0c;dp[i]就要更新为dp[i]和dp[j]1二者的最大值&#xff0c;初始化默认最大递增子序列都是1 这里遍历顺序的感觉很像多重…

termux配置nginx+php

只能以默认用户u0_axx运行,修改用户会报错An error occurred.或者file no found 安装nginx pkg install nginx安装php-fpm pkg install nginx修改nginx配置文件, nano ../usr/etc/nginx/nginx.conf#端口必须设置在1024以上(1024以下需要root,但php-fpm不能以root用户运行,n…

typescript安装后仍然不能使用tsc,如何解决

1.全局安装 npm i typescript -g 2.发现仍然不行 解决方法&#xff1a; C:\Users\你的用户名\AppData\Roaming\npm解决办法&#xff1a; 1.确定对应的文件下载了 我们发现typescript是下载了的 2.设置环境变量的path 路径为typescript下的npm 3.cmd运行

SQL字符串截取函数——Left()、Right()、Substring()用法详解

SQL字符串截取函数——Left&#xff08;&#xff09;、Right&#xff08;&#xff09;、Substring&#xff08;&#xff09;用法详解 1. LEFT() 函数&#xff1a;从字符串的左侧提取指定长度的子字符串。 LEFT(string, length)string&#xff1a;要操作的字符串。length&#x…

数字PWM直流调速系统设计(论文+源码)

2.1 系统方案设计 2.2.1开环控制方案 采用开环方案的系统架构如图2.1所示&#xff0c;这种方式不需要对直流电机的转速进行检测&#xff0c;在速度控制时单片机只需要直接发出PWM就可以实现直流电机速度的控制。这种方式整体设计难度较低&#xff0c;但是无法准确得知当前的…

Python | 学习type()方法动态创建类

getattr方法的使用场景是在访问不存在的属性时&#xff0c;会触发该方法中的处理逻辑。尤其是在动态属性获取中结合 type()动态创建类有着良好的使用关系。 type()方法常用来判断属性的类别&#xff0c;而动态创建类不常使用&#xff0c;通过如下的几个实例来学习使用&#xff…

CDP集群安全指南-静态数据加密

[一]静态数据加密的架构 CDP 支持两种加密组件&#xff0c;这些组件可以组合成独特的解决方案。在选择密钥管理系统&#xff08;KMS&#xff09;时&#xff0c;您需要决定哪些组件能够满足企业的密钥管理和加密需求。 CDP 加密组件 以下是 Cloudera 用于静态数据加密的组件描…

无线AP安装注意事项

现在的办公楼、酒店等项目中都设计含有网络无线覆盖这一项&#xff0c;在项目实施中&#xff0c;往往采用的是便捷并且后期便于网络无线设备管理的无线ap设备&#xff0c;作为前端无线信号的覆盖。在具体安装无线AP过程中&#xff0c;我们必须要注意以下几点才能保证项目实施完…

【动手学电机驱动】STM32-MBD(2)将 Simulink 模型部署到 STM32G431 开发板

STM32-MBD&#xff08;1&#xff09;安装 STM32 硬件支持包 STM32-MBD&#xff08;2&#xff09;Simulink 模型部署 【动手学电机驱动】STM32-MBD&#xff08;2&#xff09;Simulink 模型部署 1. 软硬件条件和环境测试1.1 软硬件条件1.2 开发环境测试 2. 创建基于 STM32 处理器…

adb 不是内部或外部命令,也不是可运行的程序或批处理文件。

1、问题概述&#xff1f; 本文讲述的是在window系统中安装了Android SDK之后&#xff0c;adb无法使用的情况。 在cmd中执行adb devices提示如下问题&#xff1a; adb 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。 问题&#xff1a;没有配置android sdk环…