napi系列学习进阶篇——NAPI生命周期

什么是NAPI的生命周期

我们都知道,程序的生命周期是指程序从启动,运行到最后的结束的整个过程。生命周期的管理自然是指控制程序的启动,调用以及结束的方法。 而NAPI中的生命周期又是怎样的呢?如下图所示:

从图上我们可以看出,在js应用启动时会加载napi模块,而在napi模块加载过程中会创建一个napi对象A提供给应用使用,在应用退出或者主动释放A对象前,A对象必须一直保持"活跃"状态。从A对象创建到释放的整个过程也代表着A对象的生命周期。

NAPI生命周期管理的方法

js调用时,NAPI中对象的句柄可以作为napi_value返回. 这些句柄必须保持对象“活动”,直到本机代码不再需要它们,否则可以在本机代码完成使用它们之前回收对象。
当返回对象句柄时,它们与“范围”相关联。默认范围的生命周期与本机方法调用的生命周期相关联。结果是,默认情况下,句柄保持有效,并且与这些句柄关联的对象将在本机方法调用的生命周期内保持活动状态。
但是,在许多情况下,与本地方法相比,句柄必须在更短或更长的生命周期内保持有效。此时,NAPI提供了对应的函数来改变默认句柄的寿命(即生命周期)。

设置局部生命周期

因为在napi中全部js相关的值都是一个不透明的封装,默认生命周期是和全局一致的,有时候处于安全和性能的考虑,须要将一些值的生命周期限制在必定的范围之内,此时我们就需要用到NAPI相关的接口来napi_open_handle_scope和napi_close_handle_scope建立和关闭一个上下文环境。比如:

for (int i = 0; i < 1000000; i++) {napi_handle_scope scope;napi_status status = napi_open_handle_scope(env, &scope);if (status != napi_ok) {break;}napi_value result;status = napi_get_element(e object, i, &result);if (status != napi_ok) {break;}// do something with elementstatus = napi_close_handle_scope(env, scope);if (status != napi_ok) {break;}
}

此时,因为限制了做用域,因此每一个result的生命周期都被限制在了单次循环以内。
使用到的函数:

napi_status napi_open_handle_scope(napi_env env, napi_handle_scope* result)

功能:打开一个局部的生命周期
参数说明:

  • [in] env - 当前环境变量
  • [out] result - 根据当前环境创建的生命周期变量

返回:napi_status,成功返回0,失败返回其他

napi_status napi_close_escapable_handle_scope(napi_env env, napi_handle_scope scope)

功能:关闭传入的生命周期(生命周期必须按照创建它们的相反顺序关闭)。
参数说明:

  • [in] env - 当前环境变量
  • [out] scope - 需要关闭的生命周期变量

返回:napi_status,成功返回0,失败返回其他

设置全局生命周期

在某些情况下,插件需要能够创建和引用具有比单个本地方法调用更长的生命周期的对象。例如,要创建一个构造函数并稍后在请求中使用该构造函数来创建实例,必须可以在许多不同的实例创建请求中引用该构造函数对象。如有一个napi_value变量constructor,需要将其导出到js使用:

{napi_value constructor = nullptr;...if (napi_create_reference(env, constructor, 1, &sConstructor_) != napi_ok) {    // 创建生命周期,初始引用计数设为1return nullptr;}if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) {   // 设置constructor对象相关属性并绑定到导出变量exportsreturn nullptr;}...
}

此时在其他线程或接口中就可以通过生命周期变量获取此constructor对象进行处理。
使用到的函数:

napi_status napi_create_reference(napi_env env, napi_value value, uint32_t initial_refcount, napi_ref *result)

功能:通过引用对象创建新的生命周期引用对象
参数:

  • [in] env: 当前环境变量
  • [in] value: 需要引用的对象
  • [in] initial_refcount: 引用计数初始值
  • [out] result: 新建的生命周期引用对象

返回:napi_status,成功返回0,失败其他

NAPI生命周期管理实现

这里我们以TestNapi为例(关于工程创建可以参照通过IDE开发一个Napi工程)

  • 首先新建一个 hello.cpp,实现 NAPI接口模块的注册
#include "napi/native_api.h"
#include <js_native_api_types.h>EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{// 暂未实现任何方法napi_property_descriptor desc[] = {};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 },
};
// 注册 hello模块
extern "C" __attribute__((constructor)) void RegisterHelloModule(void)
{napi_module_register(&demoModule);
}
  • 定义一个测试的类(TestNapi)
class NapiTest{
public:NapiTest(){}~NapiTest(){}static napi_value SetMsg(napi_env env, napi_callback_info info) {napi_value result = nullptr;napi_get_undefined(env, &result);char _msg[128] = {0};napi_value msgvalue;size_t argc = 1, size = 0;if (napi_get_cb_info(env, info, &argc, &msgvalue, nullptr, nullptr) !=napi_ok) {return result;}if (napi_get_value_string_utf8(env, msgvalue, _msg, sizeof(_msg), &size) != napi_ok) {return result;}return result;}static napi_value GetMsg(napi_env env, napi_callback_info info) {napi_value result;char *_msg = "hello NapiTest";if (napi_create_string_utf8(env, _msg, strlen(_msg), &result) != napi_ok) {napi_get_undefined(env, &result);return nullptr;}return result;}napi_value Create(napi_env env, void *data){}
};
  • 定义一个全局的生命周期管理的变量
static napi_ref g_Constructor = nullptr;
  • 将类的方法定义到一个napi_property_descriptor的数组
    特别申明:此步骤及以下步骤都在initi方法中完成。
static napi_value Init(napi_env env, napi_value exports)
{napi_property_descriptor desc[] = {{ "SetMsg", nullptr, NapiTest::SetMsg, nullptr, nullptr, nullptr,napi_default, nullptr },{ "GetMsg", nullptr, NapiTest::GetMsg, nullptr, nullptr, nullptr,napi_default,nullptr },};
}
  • 将测试类定义到js类,并创建调用测试类的构造函数
napi_value constructor = nullptr;
if (napi_define_class(env, NAPI_CLASS_NAME, NAPI_AUTO_LENGTH, Constructor, nullptr, sizeof(desc) / sizeof(desc[0]),desc, &constructor) != napi_ok) {return nullptr;
}

其中Constructor构造函数如下:

static napi_value Constructor(napi_env env, napi_callback_info info) {napi_value thisVar = nullptr;napi_get_undefined(env, &thisVar);napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);return thisVar;
}

如需要快速释放构建函数中创建的对象,也可以在构造函数中绑定一个类析构函数:

static napi_value Constructor(napi_env env, napi_callback_info info) {napi_value thisVar = nullptr;napi_get_undefined(env, &thisVar);napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);std::unique_ptr<NapiTest> reference = std::make_unique<NapiTest>();status = napi_wrap(env, thisVar, reinterpret_cast<void *>(reference),Destructor, nullptr, nullptr);return thisVar;
}
// 类析构函数,释放有Constructor构建时新建的对象
static void Destructor(napi_env env, void *nativeObject, void *finalize)
{NapiTest *test = reinterpret_cast<NapiTest*>(nativeObject);test->~NapiTest();
}
  • 创建生命周期
if (napi_create_reference(env, constructor, 1, &g_Constructor) != napi_ok) {return nullptr;
}
  • 将生命周期变量作为导出对象的传入属性。
if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) {return nullptr;
}
  • 设置导出对象的属性。
if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) {return nullptr;
}

到此,我们就完成了js类的定义以及相关生命周期管理的设置,该如何创建生命周期范围内的变量呢?我们可以在NapiTest类中定义一个方法,用于创建在该生命周期范围内的变量:

napi_value Create(napi_env env, void *data){napi_status status;napi_value constructor = nullptr, result = nullptr;// 获取生命周期变量status = napi_get_reference_value(env, g_Constructor, &constructor);if (status != napi_ok) {return nullptr;}/**do smoethings*/// 创建生命周期内的对象并将其返回status = napi_new_instance(env, constructor, 0, nullptr, &result);if (status != napi_ok) {return nullptr;}return result;
}

使用到的函数:

napi_status napi_get_reference_value(napi_env env, napi_ref ref, uint32_t* result)

功能:获取当前引用的napi_value数据
参数说明:

  • [in] env: 当前环境变量
  • [in] ref: 引用计数的对象
  • [out] result: 引用计数的对象绑定的napi_value数据

返回:napi_status,成功返回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.rhkb.cn/news/307954.html

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

相关文章

16、普通数组-除自身以外的数组乘积

思路 通过辅助数组的方式 第一个从左向右的辅助数组乘积第二次从右向左的辅助数组乘积对于0<i<N-1 他的数组乘积就是左边的数组乘积*右边数组乘积然后再分类讨论i0 就是右边1-N-1的数组乘积iN-1就是左边从N-2到0的数组乘积 代码如下&#xff1a; class Solution {pub…

开发日志2024-04-12

开发日志2024/04/12 1、分店月业绩和年业绩都需要添加为真实数据 **开发思路&#xff1a;**分店下所属的技师的业绩总和 代码实现&#xff1a; 前端 无 后端 //TODO 将技师多对应的积分累加到他所属的分店的月/年累计业绩销量中//TODO 查询技师所对应的分店地址String f…

【深度学习】图像超分辨

案例 7&#xff1a;图像超分辨 相关知识点&#xff1a;生成对抗网络、图像处理&#xff08;PIL&#xff09;和可视化&#xff08;matplotlib&#xff09; 1 任务目标 1.1 任务和数据简介 ​ 本次案例将使用生成对抗网络来实现 4 倍图像超分辨任务&#xff0c;输入一张低分辨…

中级物流师、高级物流师资格认证考试大纲《物流管理实务》

物流管理实务 第一章 物流市场调查 一、市场调查基本知识 二、物流市场调研 三、物流市场预测 四、物流市场调研报告 第二章 仓库规划与设计 一、仓储规划概述 二、仓库规模和数量规划 三、仓库选址规划 四、仓库的结构与布局 五、自动化立体仓库的规划与设计 第…

微信小程序报错——“errno“: 600001, “errMsg“: “request:fail -2:net::ERR_FAILED“

bug现象 微信小程序体验版和真机调试 进入小程序的时候接口就出现了这个报错 "errno": 600001, "errMsg": "request:fail -2:net::ERR_FAILED" 排查 检查是证书过期还是证书链不完整 证书的信任链完整问题&#xff0c;可以在 亚数信息-SSL/TLS安…

安装ODBC方法

1、运行 搜索 ODBC数据源管理程序 32位或者 64位 2、在用户DSN或者系统DSN选择添加&#xff08;建议前者&#xff09;&#xff0c;此处以添加access数据库的odbc驱动为例 3、安装成功

Unity让地图素材遮挡人物

点击编辑/项目设置/图形&#xff0c;透明度排序模式设置自定义轴&#xff0c;透明度排序轴Y设置为1其他为0。 此时人物和地图素材的图层排序相等&#xff0c;当人物的高度大于地图素材时&#xff0c;人物则被遮挡。

锚框(anchor box)

目标检测算法通常会在输入图像中抽样大量的区域&#xff0c;然后判断这些区域中是否包含我们感兴趣的目标&#xff0c;并调整区域边界&#xff0c;从而更准确地预测目标的真实边界框&#xff08;ground-truth bounding box)。不同的模型所使用的区域抽样方法可能不同。这里我们…

三小时零基础入门微信扫码点餐小程序 手把手带你开发一款云开发版点餐软件,店铺地图导航,外卖小程序,用户端和后厨端都有

从今天开始带领大家实现一款云开发版的点餐小程序 视频讲解&#xff1a;《云开发后台微信扫码点餐小程序cms网页管理后台》 技术选型 1&#xff0c;前端 微信小程序原生框架cssJavaScript 2&#xff0c;管理后台 云开发Cms内容管理系统web网页 3&#xff0c;数据后台 小…

【STL】list

目录 1. list的使用 1.1 list的构造 1.2 list iterator的使用 1.3 list capacity 1.4 list element access 1.5 list modifiers 1.6 list的迭代器失效 2. list的模拟实现 3. list与vector的对比 1. list的使用 1.1 list的构造 1.2 list iterator的使用 1. begin与end为…

MySQL前缀索引(3/16)

前缀索引 前缀索引&#xff1a;MySQL支持前缀索引&#xff0c;允许定义字符串的一部分作为索引。如果不指定前缀长度&#xff0c;索引将包含整个字符串。前缀索引可以节省空间&#xff0c;但可能会增加查询时的记录扫描次数&#xff08;因为会查询到多个前缀相同的数据&#x…

4.9QT

完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和密码不匹配&#xf…

Redis数据持久化 AOF RDB

Redis数据持久化 AOF RDB 1、单点 redis 的问题2、主从复制2.1 命令传播 3、Redis的持久化3.1 AOF写回策略重写机制后台重写 3.2 RDB&#xff08;默认方式&#xff09;RDB 方式&#xff1a;执行快照时&#xff0c;数据能被修改吗&#xff1f;RDB 方式总结 3.3 RDB 和 AOF 组合&…

【springboot开发】MVC和SSM

前言&#xff1a;关于MVC和SSM基本内容的梳理&#xff0c;以及两者之间的关系。 文章目录 1. 三层架构2. MVC3. SSM 1. 三层架构 三层架构是指&#xff1a; 视图层view&#xff08;表现层&#xff09;: 用于显示数据和接收用户输入的数据&#xff0c;为用户提供一种交互式操作…

684. 冗余连接

684. 冗余连接 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a;错误经验吸取 原题链接&#xff1a; 684. 冗余连接 https://leetcode.cn/problems/redundant-connection/description/ 完成情况&#xff1a; 解题思路&#xff1a; 这段…

[react优化] 避免组件或数据多次渲染/计算

代码如下 点击视图x➕1,导致视图更新, 视图更细导致a也重新大量计算!!这很浪费时间 function App() {const [x, setX] useState(3)const y x 2console.log(重新渲染, x, y);console.time(timer)let a 0for (let index 0; index < 1000000000; index) {a}console.timeE…

论文阅读AI工具链

文献检索 可以利用智谱清言来生成合适的文献检索式&#xff0c;并根据需要不断调整。 谷歌学术 在Google Scholar中进行检索时&#xff0c;您可以使用类似的逻辑来构建您的搜索式&#xff0c;但是语法会有所不同。Google Scholar的搜索框接受普通的文本搜索&#xff0c;但是…

面试-数据库基础以及MySql、ClickHost、Redis简介

面试-数据库基础以及MySql、ClickHost、Redis简介 0.数据完整性1.数据库并发控制1.1事物1.2 并发读写错误1.3 锁1.3.1 乐观锁与悲观锁1.3.2 共享锁和排他锁1.3.3 行锁与表锁1.3.4 意向锁 1.4 封锁协议与隔离级别1.5 MVCC1.5.1 概念1.5.2 当前读与快照读1.5.3 MVCC in InnoDB 2.…

基于 RisingWave 和 ScyllaDB 构建事件驱动应用

概览 在构建事件驱动应用时&#xff0c;人们面临着两大挑战&#xff1a;1&#xff09;低延迟处理大量数据&#xff1b;2&#xff09;实现流数据的实时摄取和转换。 结合 RisingWave 的流处理功能和 ScyllaDB 的高性能 NoSQL 数据库&#xff0c;可为构建事件驱动应用和数据管道…

数字乡村探索:引领农村未来发展新方向——科技创新赋能乡村现代化与农民生活品质提升之旅

目录 一、数字乡村的内涵与特点 二、数字乡村的探索进展 三、数字乡村面临的挑战与机遇 四、数字乡村的未来发展方向与路径 五、数字乡村助力农村产业升级 六、数字乡村促进城乡融合发展 七、数字乡村激发农民创新创业活力 八、数字乡村提升农民获得感和幸福感 九、展…