NAPI 类对象导出及其生命周期管理(下)

往期回顾:napi 类对象导出及其生命周期管理(上)
4. 样例工程源码剖析工程的模板是native c++,模型是stage。
源码剖析主要围绕以下几个文件
4.1. napi导出对象和生命周期管理具体实现4.1.1. 定义napitest类及方法napi.h文件内容如下:#ifndef __napi_test_h__#define __napi_test_h__#include napi/native_api.h#include #include #define napi_class_name napitestclassclass napitest {public: napitest() : menv(nullptr), mref(nullptr) { } napitest(napi_env env) : menv(env), mref(nullptr){ } ~napitest(); // 创建napitest类的实体,并将实体返回到应用端,该方法为js创建一个类实体,因此需要将该接口对外导出 static napi_value create(napi_env env, napi_callback_info info); // 初始化js类并设置对应属性并将其导出 static napi_value init(napi_env env, napi_value exports); private: // 设置数据,此方法给到js直接调用,因此需要将该接口对外导出 static napi_value setmsg(napi_env env, napi_callback_info info); // 获取数据,此方法给到js直接调用,因此需要将该接口对外导出 static napi_value getmsg(napi_env env, napi_callback_info info); // 定义js结构体时实际的构建函数 static napi_value constructor(napi_env env, napi_callback_info info); // 释放资源的函数(类似类的析构函数) static void destructor(napi_env env, void *nativeobject, void *finalize); // 生命周期变量 static napi_ref sconstructor_; // 设置和获取数据的变量 static std::string _msg; // 记录环境变量 napi_env menv = nullptr; // 记录生命周期变量 napi_ref mref = nullptr; };#endif /* __napi_test_h__ */4.1.1.1 napi_valuenode.js node-api的值用napi_value类型表示。
openharmony napi将ecmascript标准中定义的boolean、null、undefined、number、bigint、string、symbol和object八种数据类型,以及函数对应的function类型,统一封装成napi_value类型,下文中表述为js类型,用于接收arkui应用传递过来的数据及返回数据给arkui应用。
这是一个不透明的指针,用于表示javascript值。
4.1.1.2 napi_ref这是用来引用napi_value的抽象。这允许用户管理javascript值的生命周期,包括显式地定义它们的最小生命周期。
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_ref
4.1.1.3 napi_envnapi_env用于表示上下文,底层的node-api实现可以使用该上下文持久保持vm-specific的状态。
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_env
4.1.2 将napitest类定义为js类4.1.2.1在定义js类之前,需要先设置js类对外导出的方法// 在定义js类之前,需要先设置类对外导出的方法 napi_property_descriptor desc[] = { { getmsg, nullptr, napitest::getmsg, nullptr, nullptr, nullptr, napi_default, nullptr }, { setmsg, nullptr, napitest::setmsg, nullptr, nullptr, nullptr, napi_default, nullptr }, { create, nullptr, napitest::create, nullptr, nullptr, nullptr, napi_default, nullptr } };4.1.2.1.1 napi_property_descriptor参考 https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_property_descriptor
node.js node-api有一组api来获取和设置javascript对象的属性。在javascript中,属性被表示为一个键和一个值的元组。基本上,node-api中的所有属性键都可以用以下形式中的任一一种表示:
named:一个简单的utf-8编码的字符串integer-indexed:索引值,由uint32_t表示javascript value:在node-api中通过napi_value表示。它可以是一个napi_value,表示字符串、数字或符号。typedef struct { // utf8name和name其中一个必须是null const char* utf8name; napi_value name; napi_callback method; napi_callback getter; napi_callback setter; napi_value value; napi_property_attributes attributes; void* data;} napi_property_descriptor;参数解析:
utf8name:在定义js类之前设置的js类对外导出的方法名字,编码为utf8。必须为该属性提供utf8name或name中的一个。(utf8name和name其中一个必须是null)name:可选的napi_value,指向一个javascript字符串或符号,用作属性的键。必须为该属性提供utf8name或name中的一个。method:将属性描述符对象的value属性设置为method表示的javascript函数。如果传入这个参数,将value、getter和setter设置为null(因为这些成员不会被使用)。attributes:与特定属性相关联的属性。data:调用函数时传递给method、getter和setter的callback data。4.1.2.2 定义与c++类相对应的javascript类napi_value constructor = nullptr; // 定义与c++类相对应的javascript类 if (napi_define_class(env, napi_class_name, napi_auto_length, constructor, nullptr, sizeof(desc) / sizeof(desc[0]), desc, &constructor) != napi_ok) { // !=用来检查两个操作数的值是否相等,如果不相等则条件为真 return nullptr; }4.1.2.2.1 napi_define_classnapi_define_class函数说明:
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_define_class
napi_status napi_define_class(napi_env env, const char* utf8name, size_t length, napi_callback constructor, void* data, size_t property_count, const napi_property_descriptor* properties, napi_value* result);功能:定义与c ++ 类相对应的javascript类。
参数说明:
[in] env: 调用api的环境
[in] utf8name: c ++ 类的名称
[in] length: c ++ 类的名称的长度,默认自动长度使用napi_auto_length
[in] constructor: 处理c ++ 类实例构造的回调函数 (因为constructor函数被napi_define_class调用了)。在导出c ++ 类对象时,这个函数必须是带有napi_callback签名(constructor函数有napi_callback签名是指要满足typedef napi_value (*napi_callback)(napi_env, napi_callback_info);的形式)的静态成员。不能使用c ++ 的类构造函数。
[in] data: 作为回调信息的数据属性传递给构造函数回调的可选数据
[in] property_count: 属性数组中参数的个数
[in] properties: 属性数组,具体看代码中napi_property_descriptor部分
[out] result: 通过类构造函数绑定类实例的napi_value对象
返回:如果api调用成功返回napi_ok。
js构造函数
如果一个js函数被使用new操作符来调用了,那么这个函数就称之为js构造函数
c++类回调函数
我们调用别人的api叫call,调用的第三方api调用我们的函数叫回调(callback)
4.1.2.3 实现js类的构造函数当arkts应用在js端通过new方法获取类对象的时候,此时会调用 napi_define_class 中设置的 constructor 回调函数,该函数实现方法如下:
napi_value napitest::constructor(napi_env env, napi_callback_info info){ napi_value undefinevar = nullptr, thisvar = nullptr; napi_get_undefined(env, &undefinevar); // 获取传入的参数对象,对象不为空,根据该参数创建实例并并绑定到该对象 if (napi_get_cb_info(env, info, nullptr, nullptr, &thisvar, nullptr) == napi_ok && thisvar != nullptr) { // 创建napitest 实例 napitest *reference = new napitest(env); // 绑定实例到对象并获取对象的生命周期 if (napi_wrap(env, thisvar, reinterpret_cast(reference), napitest::destructor, nullptr, &(reference->mref)) == napi_ok) { return thisvar; } return thisvar; } return undefinevar;}void napitest::destructor(napi_env env, void *nativeobject, void *finalize){ // 释放资源 napitest *test = reinterpret_castnapitest::destructo方法是用来释放创建的对象:void napitest::destructor(napi_env env, void *nativeobject, void *finalize){ // 类析构函数,释放资源 napitest *test = reinterpret_cast4.1.2.3.1 napi_wrapnapi_status napi_wrap(napi_env env, napi_value js_object, void* native_object, napi_finalize finalize_cb, void* finalize_hint, napi_ref* result);功能:将c++类实例绑定到js对象,并关联对应的生命周期
参数说明:
[in] env: 调用api的环境[in] js_object: 绑定native_object的js对象[in] native_object: c++类实例对象[in] finalize_cb: 释放实例对象的回调函数[in] finalize_hint: 传递给回调函数的数据[out] result: 绑定js对象的引用返回:调用成功返回0,失败返回其他
4.1.2.3.2 napi_get_cb_infonapi提供了napi_get_cb_info()方法可从napi_callback_info中获取参数列表、this及其他数据。这个方法在constructor回调函数中使用,从给定的回调信息中检索有关调用的详细信息,如参数和this指针。
napi_status napi_get_cb_info(napi_env env, napi_callback_info cbinfo, size_t* argc, napi_value* argv, napi_value* this_arg, void** data)参数说明:
[in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可[in] cbinfo: napi_callback_info对象,上下文的信息[in-out] argc: argv数组的长度。若napi_callback_info中实际包含的参数的个数大于请求的数量argc,将只复制argc的值所指定数量的参数只argv中。若实际的参数个数小于请求的数量,将复制全部的参数,数组多余的空间用空值填充,并将参数实际长度写入argc。[out] argv: 用于接收参数列表[out] this_arg: 用于接收this对象[out] data: napi的上下文数据 返回值:返回napi_ok表示转换成功,其他值失败。下面的返回napi_status方法一样。4.1.3 导出js类// 创建生命周期,初始引用计数设为1 if (napi_create_reference(env, constructor, 1, &sconstructor_) != napi_ok) { return nullptr; } // 设置napitest对象相关属性并绑定到导出变量exports if (napi_set_named_property(env, exports, napi_class_name, constructor) != napi_ok) { return nullptr; }4.1.3.1 在设置js类导出前,需要先创建生命周期if (napi_create_reference(env, constructor , 1, &sconstructor_) != napi_ok) { return nullptr;}constructor 定义js类时返回的代表类的构造函数的数据sconstructor_ 生命周期变量4.1.3.1.1 napi_create_referencenapi_create_reference为对象创建一个reference,以延长其生命周期。调用者需要自己管理reference生命周期。
napi_create_reference函数说明:
napi_extern napi_status napi_create_reference(napi_env env, napi_value value, uint32_t initial_refcount, napi_ref* result);功能:通过引用对象创建新的生命周期引用对象
[in] env: 调用 api 的环境
[in] value: napi_value表示我们要引用的对象
[in] initial_refcount: 生命周期变量的初始引用计数
[out] result: 新建的生命周期引用对象
返回 napi_ok 这个api就是成功的.
4.1.3.2 将生命周期变量作为导出对象的传入属性,并将js类导出到exports中// 设置constructor对象相关属性并绑定到导出变量exportsif (napi_set_named_property(env, exports, napi_class_name, constructor) != napi_ok) { return nullptr;}4.1.3.2.1 napi_set_named_property为给定对象的属性设置一个名称。
napi_status napi_set_named_property(napi_env env, napi_value object, const char* utf8name, napi_value value);[in] env: 调用api的环境
[in] object: napitest对象相关属性要绑定的属性值
[in] utf8name: js类的名称
[in] value: 要引用的对象
返回 napi_ok 则这个api是成功的
4.1.3.3 设置导出对象的属性hello.cpp中
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);4.1.3.3.1 napi_define_propertieshttps://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_define_properties
napi_status napi_define_properties(napi_env env, napi_value object, size_t property_count, const napi_property_descriptor* properties);作用:批量的向给定object中定义属性
[in] env: 调用api的环境[in] object: js对象相关属性的导出变量[in] property_count: 属性数组中的元素数[in] properties: 属性数组4.1.4 创建类的实例对象arkts应用除了调用new方法获取类的实例外,我们也可以提供一些方法让arkts应用获取对应的类的实例,如在我们的napitest类中,定义了一个create方法,该方法实现了napitest类实例的获取。具体实现如下:napi_value napitest::create(napi_env env, napi_callback_info info) { napi_status status; napi_value constructor = nullptr, result = nullptr; // 获取生命周期变量 status = napi_get_reference_value(env, sconstructor_, &constructor); // 创建生命周期内的实例对象并将其返回 status = napi_new_instance(env, constructor, 0, nullptr, &result); auto napitest = new napitest(); // 绑定实例类创建napitest到导出的对象result if (napi_wrap(env, result, reinterpret_cast(napitest), destructor, nullptr, &(napitest->mref)) == napi_ok) { return result; } return nullptr;}在napi接口的注册中将该方法以接口的方式导出,应用层就可以直接调用该接口并获取到该类的实例对。
特别说明:如果单独实现了一个类实例获取的方法,那么js的类构造函数可以不实现(也就是定义js结构体时实际的构建函数constructor及释放资源的函数destructor的代码够可以不写)
4.1.4.1 napi_get_reference_valuehttps://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_get_reference_value
函数说明:
napi_extern napi_status napi_get_reference_value(napi_env env, napi_ref ref, napi_value* result);作用:获取与reference相关联的js对象[in] env: 调用api的环境[in] ref: 生命周期管理的变量[out] result: 对象引用的reference.4.1.4.2 napi_new_instancehttps://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_new_instance
napi_status napi_new_instance(napi_env env, napi_value cons, size_t argc, napi_value* argv, napi_value* result)作用:通过给定的构造函数,构建一个对象[in] env: 调用api的环境[in] cons: napi_value表示要作为构造函数调用的 javascript 函数[in] argc: argv 数组中的元素计数[in] argv: javascript 值数组,表示构造函数的参数napi_value。[out] result: napi_value表示返回的 javascript 对象4.2 index.d.ts声明文件编写使用napi框架代码生成工具,可以根据.h生成.d.ts
https://gitee.com/openharmony/napi_generator/blob/master/docs/instruction_zh.md
export const create : () => napitest;export class napitest { setmsg(msg: string): void; getmsg(): string;}也可以写成
export class napitest { create(); setmsg(msg: string): void; getmsg(): string;}4.3 cmakelists.txt文件# the minimum version of cmake.cmake_minimum_required(version 3.4.1)project(objectwraptest)set(nativerender_root_path ${cmake_current_source_dir})# 头文件路径include_directories(${nativerender_root_path} ${nativerender_root_path}/include)# 动态库源文件add_library(entry shared hello.cpp napitest.cpp)# 依赖libace_napi.z.so动态库target_link_libraries(entry public libace_napi.z.so )4.4 index.ets文件// 让ide不检查文件语法// @ts-nocheck import testnapi from libentry.so;@entry@componentstruct index { @state message: string = '导出对象' @state nativepointer:number = 0// 创建对象tt tt = testnapi.create(); build() { row() { column() { text(this.message) .fontsize(50) .fontweight(fontweight.bold) .onclick(() => { console.info([napitest] test napi 2 + 3 = + testnapi.add(2, 3)); try{ if (this.nativepointer == 0) { // log打印,在程序中添加 log console.info([napitest] test napi add(2, 3) 1); this.nativepointer = testnapi.add(2, 3) console.info([napitest] test napi add(2, 3) 2); this.tt.setmsg(2+3) console.info([napitest] test napi add(2, 3) 3); } else { console.info([napitest] test napi add(0, 0) 1); this.nativepointer = testnapi.add(0, 0) console.info([napitest] test napi add(0, 0) 2); this.tt.setmsg(4+5) console.info([napitest] test napi add(0, 0) 3); } } catch(e) { console.info([napitest]test napi error + json.stringify(e)); } console.info([napitest]test napi + this.tt.getmsg() + = + this.nativepointer); }) } .width('100%') } .height('100%') }}知识点附送napi接口名称https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/native-lib/third_party_napi/napi.md
napi框架api调用值得借鉴的示例工程https://gitee.com/jiangtao92/node-api-test-case

自动驾驶视觉芯片领域存在后来居上吗
阿里巴巴达摩院CTO谈中天微:芯片战略要长期布局与规划
华特电动如何为前途汽车超跑做Pack?
矽海达科技Sihid远程控制单元简介
3G接入WLAN测试方案 WLAN测试基本概念
NAPI 类对象导出及其生命周期管理(下)
浅谈蓝牙技术的优势及应用
如何计算减速电机的减速比?顺力电机
气体检测仪的使用寿命大概是多久?-欧森杰
区块链将会从五个方面影响房地产市场
小米折叠屏手机或将4月底发布_价格六千起步很感人
C6748 DSP性能升级平台实力推荐
LED的寿命
华为辟谣:否认轮值董事长徐直军离职传闻
使用重铜制造的PCB
奥宝最尖端Ultra Fusion 200自动化光学检测系统
Intersil推出业内首款集成ADC驱动器的40V低噪声精密仪表放大器
NVIDIA GPU超强AI算力推动线下零售数字化转型
如何导入S7-200 Smart变量
今年Q3全球半导体设备出货金额达90.6亿美元