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

news2024/12/22 19:49:23

什么是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 element
  status = 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) {    // 创建生命周期,初始引用计数设为1
      return nullptr;
  }
  if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) {   // 设置constructor对象相关属性并绑定到导出变量exports
      return 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.coloradmin.cn/o/1592142.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

foreach无法修改数组值解决方案

效果展示&#xff1a; 解决办法&#xff1a; this.sportList.forEach((item,index) >{let that this;if(item.idinfo.id) {that.sportList[index].sportTime e.detail.value} }) 这里小编解释下&#xff0c;将this赋值给that通常是为了在回调函数或者异步代码中保持对Vu…

C++11 设计模式4. 抽象工厂(Abstract Factory)模式

问题的提出 从前面我们已经使用了工厂方法模式 解决了一些问题。 现在 策划又提出了新的需求&#xff1a;对于各个怪物&#xff0c;在不同的场景下&#xff0c;怪物的面板数值会发生变化&#xff0c; //怪物分类&#xff1a;亡灵类&#xff0c;元素类&#xff0c;机械类 …

Unity让地图素材遮挡人物

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

使用ArrayList.removeAll(List list)导致的机器重启

背景 先说一下背景&#xff0c;博主所在的业务组有一个核心系统&#xff0c;需要同步两个不同数据源给过来的数据到redis中&#xff0c;但是每次同步之前需要过滤掉一部分数据&#xff0c;只存储剩下的数据。每次同步的数据与需要过滤掉的数据量级大概在0-100w的数据不等。 由…

【C语言】字符串函数和内存函数及其模拟实现

文章目录 前言 一、常见字符串库函数1.strlen函数2.长度不受限制的字符串函数2.1 strcpy2.2 strcat2.3 strcmp 3.长度受限制的字符串函数3.1 strncpy3.2 strncat3.3 strncmp 二、字符串查找函数strstrstrtok 三、strerror函数四、内存操作函数1.memcpy2.memmove3.memcmp 五、字…

Unity之Unity面试题(三)

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity之Unity面试题&#xff08;三&#xff09; TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、心进取…

简单工厂模式大解析:让代码创造更高效、更智能!

个人主页: danci_ &#x1f525;系列专栏&#xff1a;《设计模式》《MYSQL应用》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自热榜文章&#xff1a;探索设计模式的魅力&#xff1a;简单工厂模式 简单工厂模式&#x…

【零基础学数据结构】双向链表

1.双向链表的概念 1.1头节点 1.2带头双向循环链表 注意&#xff1a; 哨兵位创建后&#xff0c;首尾连接自己 1.3双链表的初始化 // 双向链表的初始化 void ListInit(ListNode** pphead) {// 给双链表创建一个哨兵位*pphead ListBuyNode(-1); } 2.双向链表的打印 // 双向…

PE文件的分析和构造超详细过程

本文详细讲述如何从0构造一个PE文件&#xff0c;运行该文件会弹出一个HelloPE的窗口 目录 预备知识 1. 构造DOS头IMAGE_DOS_HEADER 1.1 构造DOS_MZ头 1.2 构造DOS_STUB 2、构造PE头IMAGE_NT_HEADERS 248字节 2.1 signature 2.2 IMAGE_FILE_HEADER 2.3 IMAGE_OPTI…

MOS管电路的应用及注意事项

1,buck电路的上下桥死区时间多少合适 死区时间由驱动芯片控制的&#xff0c;外围电路增加阻容会导致上升沿时间变长 死区时间从单片机PWM到驱动电路再到MOS管的栅极都有一定的硬件延时&#xff0c;所以具体时间需要根据调试确定。 例如充电芯片的buck电路&#xff0c;有死区配置…

mysql8.0高可用集群架构实战

MySQL :: MySQL Shell 8.0 :: 7 MySQL InnoDB Cluster 基本概述 InnoDB Cluster是MySQL官方实现高可用读写分离的架构方案,其中包含以下组件 MySQL Group Replication,简称MGR,是MySQL的主从同步高可用方案,包括数据同步及角色选举Mysql Shell 是InnoDB Cluster的管理工具,用…

数据库系统概论(超详解!!!)第四节 数据库安全性

问题的提出&#xff1a; 数据库的一大特点是数据可以共享 数据共享必然带来数据库的安全性问题 数据库系统中的数据共享不能是无条件的共享。 1.数据库的安全概述 数据库的安全性是指保护数据库以防止不合法使用所造成的数据泄露、更改或破坏 。 系统安全保护措施是否有效…

基于ssm的土家风景文化管理平台(java源码+文档)

项目简介 土家风景文化管理平台实现了以下功能&#xff1a; 土家风景文化管理平台的主要使用者分为管理员&#xff1a;管理员使用本平台涉到的功能主要有&#xff1a;首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;景点分类管理&#xff0c;热门景点管理&#xf…

深度学习学习日记4.7

1.梯度下降 w 新 w旧 - 学习率梯度 训练的目的就是让 loss 减小 2.前向传播进行预测&#xff0c; 反向传播进行训练(每一个参数通过梯度下降进行更新参数)&#xff0c;(1前向传播 2求 loss 3反向传播 4梯度更新) 能够让损失下降的参数&#xff0c;就是更好的参数。 损失…

基于SSM物业管理系统

摘要 进入二十一世纪以来&#xff0c;计算机技术蓬勃发展&#xff0c;人们的生活发生了许多变化。很多时候人们不需要亲力亲为的做一些事情&#xff0c;通过网络即可完成以往需要花费很多时间的操作&#xff0c;这可以提升人们的生活质量。计算机技术对人们生活的改变不仅仅包…

单列模式1.0

单列模式 单例模式能保证某个类在程序中只存在唯⼀⼀份实例, ⽽不会创建出多个实例 1.饿汉模式 只要程序一启动就会立即创建出一个对象 class Signleton{private static Signleton instancenew Signleton();//防止在以后的代码中再创建对象&#xff0c;我们将构造方法private,…

FreeFileSync|本地自动备份设置教程,终于可以不用手动同步了

前言 昨天小白给各位小伙伴分享了FreeFileSync软件&#xff0c;由于篇幅过长&#xff0c;所以整个教程中并没有教小伙伴们如何设置自动同步的办法。 今天小白就来唠唠&#xff1a;如何让FreeFileSync自动同步。 教程分为几种&#xff1a; 开机自动同步 开机之后自动执行一次…

LeetCode617:合并二叉树

题目描述 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff09;。你需要将这两棵树合并成一棵新二叉树。合并的规则是&#xff1a;如果两个节点重…

天地人和•大道不孤——卢禹舜中国画作品展在重庆美术馆隆重开幕

2024年4月12日&#xff0c;由中国国家画院、重庆市文化和旅游发展委员会主办&#xff0c;重庆美术馆&#xff08;重庆画院、重庆国画院&#xff09;、北京八荒锦绣美术馆、中国国际文化交流基金会卢禹舜艺术基金承办的“天地人和•大道不孤——卢禹舜中国画作品展”开幕式在重庆…

SF58-ASEMI适配器二极管SF58

编辑&#xff1a;ll SF58-ASEMI适配器二极管SF58 型号&#xff1a;SF58 品牌&#xff1a;ASEMI 封装&#xff1a;DO-27 最大平均正向电流&#xff08;IF&#xff09;&#xff1a;5A 最大循环峰值反向电压&#xff08;VRRM&#xff09;&#xff1a;600V 最大正向电压&…