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

相关文章

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Java第二阶段---09类和对象---第三节 构造方法

第三节 构造方法 1.概念 构造方法是一种特殊的方法,主要用于创建对象以及完成对象的属性初始化操作。构造方法不能被对象调用。 2.语法 //[]中内容可有可无 访问修饰符 类名([参数列表]){ } 3.示例 public class Car {     //车特征(属性)     public String name;//车名   可以直接拿来用 说明它有初始值     pu

Maven(插件配置和生命周期的绑定)

1.这篇文章很好,介绍的maven插件的。 2.maven的source插件为例,可以把源代码打成包。 Goals Overview就可以查看该插件下面所有的目标。 这里我们要使用的是source:jar-no-fork。 3.查看source插件的example,然后配置到riil-collect.xml中。  <build>   <plugins>    <pl

Sentinel 高可用流量管理框架

Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 Sentinel 具有以下特性: 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应

NGINX轻松管理10万长连接 --- 基于2GB内存的CentOS 6.5 x86-64

转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=190176&id=4234854 一 前言 当管理大量连接时,特别是只有少量活跃连接,NGINX有比较好的CPU和RAM利用率,如今是多终端保持在线的时代,更能让NGINX发挥这个优点。本文做一个简单测试,NGINX在一个普通PC虚拟机上维护100k的HTTP

PMBOK® 第六版 规划进度管理

目录 读后感—PMBOK第六版 目录 规划进度管理主要关注为整个项目期间的进度管理提供指南和方向。以下是两个案例,展示了进度管理中的复杂性和潜在的冲突: 案例一:近期,一个长期合作的客户因政策要求,急需我们为多家医院升级一个小功能。在这个过程中出现了三个主要问题: 在双方确认接口协议后,客户私自修改接口并未通知我们,直到催进度时才发现这个问题关于UI设计的部分,后台开发人员未将其传递给

PHP原理之内存管理中难懂的几个点

PHP的内存管理, 分为俩大部分, 第一部分是PHP自身的内存管理, 这部分主要的内容就是引用计数, 写时复制, 等等面向应用的层面的管理. 而第二部分就是今天我要介绍的, zend_alloc中描写的关于PHP自身的内存管理, 包括它是如何管理可用内存, 如何分配内存等. 另外, 为什么要写这个呢, 因为之前并没有任何资料来介绍PHP内存管理中使用的策略, 数据结构, 或者算法. 而在我们