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

2024-04-13 03:20

本文主要是介绍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 <js_native_api_types.h>#include <iostream>#define NAPI_CLASS_NAME "NapiTestClass"class 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_value
  • Node.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值的生命周期,包括显式地定义它们的最小生命周期。
4.1.1.3 napi_env
  • napi_env用于表示上下文,底层的Node-API实现可以使用该上下文持久保持VM-specific的状态。
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

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其中一个必须是NULLconst 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_class

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<void *>(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_cast<NapiTest*>(nativeObject);test->~NapiTest();
}
  • NapiTest::Destructo方法是用来释放创建的对象:
void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize)
{// 类析构函数,释放资源NapiTest *test = reinterpret_cast<NapiTest*>(nativeObject);test->~NapiTest();
}
4.1.2.3.1 napi_wrap
napi_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_info

NAPI提供了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类
    // 创建生命周期,初始引用计数设为1if (napi_create_reference(env, constructor, 1, &sConstructor_) != napi_ok) {return nullptr;}// 设置NapiTest对象相关属性并绑定到导出变量exportsif (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_reference

napi_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对象相关属性并绑定到导出变量exports
if (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_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到导出的对象resultif (napi_wrap(env, result, reinterpret_cast<void *>(napiTest), Destructor,nullptr, &(napiTest->mRef)) == napi_ok) {return result;}return nullptr;
}
  • 在napi接口的注册中将该方法以接口的方式导出,应用层就可以直接调用该接口并获取到该类的实例对。
    特别说明:如果单独实现了一个类实例获取的方法,那么js的类构造函数可以不实现(也就是定义js结构体时实际的构建函数Constructor及释放资源的函数Destructor的代码够可以不写)
4.1.4.1 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_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

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// 创建对象tttt = 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打印,在程序中添加 logconsole.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%')}}

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

这篇关于NAPI 类对象导出及其生命周期管理(下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/899006

相关文章

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2

Java导出Excel动态表头的示例详解

《Java导出Excel动态表头的示例详解》这篇文章主要为大家详细介绍了Java导出Excel动态表头的相关知识,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录前言一、效果展示二、代码实现1.固定头实体类2.动态头实现3.导出动态头前言本文只记录大致思路以及做法,代码不进

Linux内存泄露的原因排查和解决方案(内存管理方法)

《Linux内存泄露的原因排查和解决方案(内存管理方法)》文章主要介绍了运维团队在Linux处理LB服务内存暴涨、内存报警问题的过程,从发现问题、排查原因到制定解决方案,并从中学习了Linux内存管理... 目录一、问题二、排查过程三、解决方案四、内存管理方法1)linux内存寻址2)Linux分页机制3)

JSON字符串转成java的Map对象详细步骤

《JSON字符串转成java的Map对象详细步骤》:本文主要介绍如何将JSON字符串转换为Java对象的步骤,包括定义Element类、使用Jackson库解析JSON和添加依赖,文中通过代码介绍... 目录步骤 1: 定义 Element 类步骤 2: 使用 Jackson 库解析 jsON步骤 3: 添

详解Vue如何使用xlsx库导出Excel文件

《详解Vue如何使用xlsx库导出Excel文件》第三方库xlsx提供了强大的功能来处理Excel文件,它可以简化导出Excel文件这个过程,本文将为大家详细介绍一下它的具体使用,需要的小伙伴可以了解... 目录1. 安装依赖2. 创建vue组件3. 解释代码在Vue.js项目中导出Excel文件,使用第三

关于Maven生命周期相关命令演示

《关于Maven生命周期相关命令演示》Maven的生命周期分为Clean、Default和Site三个主要阶段,每个阶段包含多个关键步骤,如清理、编译、测试、打包等,通过执行相应的Maven命令,可以... 目录1. Maven 生命周期概述1.1 Clean Lifecycle1.2 Default Li

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取

Python实现将实体类列表数据导出到Excel文件

《Python实现将实体类列表数据导出到Excel文件》在数据处理和报告生成中,将实体类的列表数据导出到Excel文件是一项常见任务,Python提供了多种库来实现这一目标,下面就来跟随小编一起学习一... 目录一、环境准备二、定义实体类三、创建实体类列表四、将实体类列表转换为DataFrame五、导出Da