napi系列学习进阶篇——NAPI 导出类对象

简介

js调用napi的数据,对于简单的数据类型,只需要napi返回对应类型的napi_value数据即可 (详情参照napi数据类型类型与同步调用)。但是对于一些复杂的数据类型(如我们常用C++的类对象),是不能直接返回一个napi_value数据的。这时我们需要对这些数据进行一系列操作后将其导出,这样js才能使用导出后的对象。 本文以导出类对象为例来说明napi导出对象的具体过程。
类对象导出的具体过程:

NAPI导出类对象具体实现

这里我们以导出NapiTest类为例说明导出一个类的实现过程

定义NapiTest类以及相关方法

NapiTest类主要实现了接收js设置的数据并将该数据返回到js应用中,具体定义如下(NapiTest.h):

class NapiTest {
public:NapiTest() : mEnv(nullptr), mRef(nullptr) {}~NapiTest();static napi_value Create(napi_env env, napi_callback_info info);  // 创建NapiTest类的实体,并将实体返回到应用端,该方法为js创建一个类实体,因此需要将该接口对外导出static napi_value Init(napi_env env, napi_value exports);         // 初始化js类并设置对应属性并将其导出。private: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);     // 定义js结构体时实际的构建函数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;        // 记录生命周期变量
};

将NapiTest定义为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 },
}
  • 定义js类
napi_value mConstructor = nullptr;
if (napi_define_class(env, NAPI_CLASS_NAME, NAPI_AUTO_LENGTH, Constructor, nullptr,sizeof(desc) / sizeof(desc[0]), desc, &mConstructor) != napi_ok) {return nullptr;
}

使用到函数说明:

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++类定义为js的类
参数说明:

*   [in] env: 调用api的环境
*   [in] utf8name: C++类的名字
*   [in] length: C++类名字的长度,默认自动长度使用NAPI_AUTO_LENGTH
*   [in] constructor: 处理构造类实例的回调函数
*   [in] data: 作为回调信息的数据属性传递给构造函数回调的可选数据
*   [in] property_count: 属性数组参数中的个数
*   [in] properties: 属性数组
*   [out] result: 通过类构造函数绑定类实例的napi_value对象

返回:调用成功返回0,失败返回其他

  • 实现js类的构造函数

当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);// 绑定实例类创建NapiTest到导出的对象resultif (napi_wrap(env, thisVar, reinterpret_cast<void *>(reference),NapiTest::Destructor, nullptr, &(reference->mRef)) == napi_ok) {return thisVar;}return thisVar;}return undefineVar;
}

其中NapiTest::Destructo方法是用来释放创建的对象:

void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize)
{NapiTest *test = reinterpret_cast<NapiTest*>(nativeObject);test->~NapiTest();
}

使用到函数说明:

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: 绑定C++类实例的js对象
*   [in] native_object: 类实例对象
*   [in] finalize_cb: 释放实例对象的回调函数
*   [in] finalize_hint: 传递给回调函数的数据
*   [out] result: 绑定js对象的引用返回:调用成功返回0,失败返回其他

导出js类

  • 创建生命周期(生命周期相关可以参考文档napi生命周期)

在设置类导出前,需要先创建生命周期

if (napi_create_reference(env, mConstructor , 1, &sConstructor_) != napi_ok) {return nullptr;
}

mConstructor 定义js类时返回的代表类的构造函数的数据
sConstructor_ 生命周期变量

  • 将类导出到exports中 将类以属性值的方式导出
if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) !=  napi_ok) {return nullptr;
}

通过以上步骤,我们基本实现了NapiTest这个类的导出。
注意:以上实现都是在类的Init方法中,我们只需要在NAPI注册的接口中调用该Init即可。

创建类的实例对象

js应用除了调用new方法获取类的实例外,我们也可以提供一些方法让js应用获取对应的类的实例,如在我们的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的类构造函数可以不实现。

实现NAPI接口的注册

我们已helloworld为列,

  • 新建一个hello.cpp,定义模块
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 },
};
  • 实现模块的Init
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{napi_property_descriptor desc[] = {{ "create", nullptr, NapiTest::Create, nullptr, nullptr, nullptr, napi_default, nullptr }   // 单独导出 create 方法,js应用可以直接调用Create方法获取类实例};napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);return NapiTest::Init(env, exports);    // 导出类以及类的方法
}
EXTERN_C_END
  • 模块注册
// 注册 hello模块
extern "C" __attribute__((constructor)) void RegisterHelloModule(void)
{napi_module_register(&demoModule);
}

至此,我们完成了整个napi接口注册以及napi类的导出。

应用调用NAPI实例

导出接口

在使用该NAPI的时候,我们需要在ts文件(路径在\entry\src\main\cpp\types\libentry\index.d.ts),声明以下内容:

export const create : () => NapiTest;
export class  NapiTest {setMsg(msg: string): void;getMsg(): string;
}

该文件申明了NAPI接口中导出的方法和类

###应用调用

新建一个helloworld的ETS工程,该工程中包含一个按键,我们可以通过该按键进行数据的在native C++中存储和获取

  • 导出napi对应的库(之前NAPI接口生成的库名为libentry.so)
import testNapi from "libentry.so";
  • 定义变量 tt
struct Index {@State message: string = 'Hello World'@State flag:number = 0tt = testNapi.create();build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold).onClick(() => {})}.width('100%')}.height('100%')}
  • 在按键中调用对应的接口并输出内容
if (this.falg == 0) {this.flag = 2this.tt.setMsg("1+1")
} else {this.flag = 0this.tt.setMsg("1-1")
}
console.info("[NapiTest]:" + this.tt.getMsg() + " = " + this.flag);

通过IDE LOG信息可以查看到,当按多次下按钮时,出现交替以下信息:

02200/JsApp: [NapiTest]: 1+1 = 2
02200/JsApp: [NapiTest]: 1-1 = 0 

为了能让大家更好的学习鸿蒙(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.鸿蒙南向开发方向

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣2923、2924.找到冠军I、II---(简单题、中等题、Java、拓扑排序)

目录 一、找到冠军I 思路描述&#xff1a; 代码&#xff1a; 二、找到冠军II 思路描述&#xff1a; 代码&#xff1a; 一、找到冠军I 一场比赛中共有 n 支队伍&#xff0c;按从 0 到 n - 1 编号。 给你一个下标从 0 开始、大小为 n * n 的二维布尔矩阵 grid 。对于满足…

C++常考面试题(第二篇)

【题目 16】拷贝构造函数的格式和作用&#xff0c;自动调用拷贝构造函数的几种情形&#xff1f; 格式&#xff1a;没有返回值 函数名和类名相同 参数&#xff1a;const person& 类型引用 作用&#xff1a;逐个给成员变量进行赋值三种情形下会调用拷贝构造函数 (1) 当用一…

c++之stack_queue与反向迭代器的实现

目录 1. 简单介绍stack与queue的使用 1.1 stack的介绍与使用 stack的介绍 stack的使用 相关题目 1.2 queue的介绍与使用 queue的介绍 queue的使用 相关题目 2.stack与queue的模拟实现 容器适配器 2.1 stack的模拟实现 2.2 queue的模拟实现 2.3 priority_queu…

嵌入式STM32F407CET6移植OpenHarmony系统方法

第一:【实验目的】 1、STM32F407CET6开发版移植鸿蒙系统的方式 第二:【实验原理】 涉及到原理图添加原理图--普通STM32F407原理图第三:【实验步骤】 一、下载LiteOs源码,复制到到虚拟机中并解压 https://gitee.com/LiteOS/LiteOS

WebLogic-XMLDecoder(CVE-2017-10271)反序列化漏洞分析及复现

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

SS3D翻译

SS3D networka missingannotated instance mining module 缺失注释实例挖掘模块Score-based filteringIoU-guided suppressionFinal-step instance bank processing a reliable background mining module 可靠背景挖掘模块point cloud filling data augmentation 点云填充数据增…

(学习日记)2024.04.16:UCOSIII第四十四节:内存管理

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

HackTheBox-Machines--MonitorsTwo

文章目录 0x01 信息收集0x02 CVE-2022-46169 漏洞利用0x03 权限提升0x04 提升到root权限 MonitorsTwo 测试过程 0x01 信息收集 a.端口扫描: 发现22、80端口    b.信息收集: 1.2.22 Cacti信息收集 nmap -sC -sV 10.129.186.1321.访问 10.129.186.132&#xff0c;为 1.2.22 Ca…

底层文件操作的各种函数(二)------printf,fprintf,sprintf,scanf,fscanf,sscanf的对比以及文件缓冲区

偷得几日清闲&#xff0c;又因一瞬之间对蹉跎时间的愧疚&#xff0c;由此而来到CSDN这个高手云集和新手求学的平台来也写上那么一篇博客。虽然自己的博客那么久不温不热&#xff0c;但坚持写作&#xff0c;巩固自己就好。今天要讲的是续接上一篇文章的补充与继续吧。上期文章&a…

免费升级至HTTPS协议教程

一、前言 HTTPS协议以其安全性和数据加密特性&#xff0c;逐渐取代HTTP成为互联网通信的主流协议。本文将为您简洁明了地介绍如何免费升级至HTTPS协议。 二、获取免费SSL证书 选择证书提供商&#xff1a;如JoySSL等提供免费SSL证书的服务。 免费申请地址https://www.joyssl.…

太阳光光照试验耐久性老化试验使用太阳光模拟器系统

上海科迎法电气科技有限公司生产的太阳光模拟器系统主要应用于太阳能研究、材料研究、光伏组件测试、空间环境模拟器、植物生长研究、光热模拟等领域&#xff0c;主要表现特征为&#xff1a; 1. 太阳能研究&#xff1a;可用于模拟不同光照条件下太阳能电池的性能测试和研究&am…

Golang 开发实战day10 - Maps

&#x1f3c6;个人专栏 &#x1f93a; leetcode &#x1f9d7; Leetcode Prime &#x1f3c7; Golang20天教程 &#x1f6b4;‍♂️ Java问题收集园地 &#x1f334; 成长感悟 欢迎大家观看&#xff0c;不执着于追求顶峰&#xff0c;只享受探索过程 Golang 教程10 - Maps 1. M…