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 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.鸿蒙南向开发方向

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

WordPress 图片压缩插件:Compress JPEG PNG images 使用方法

插件介绍 Compress JPEG & PNG images是一款非常好用的图片压缩插件:&#xff0c;非常值得大家安装使用&#xff1b;特别是图片类型网站。其实我们很多服务器磁盘空间是不在乎多那么几十 MB 大小的&#xff0c;但是压缩了图片能提升网站速度&#xff0c;节省宽带&#xff…

入门:多层感知器Multiple-Layer Perceiver, MLP

本文将简单介绍多层感知器&#xff08;MLP&#xff09;的基本概念、原理和应用。MLP是一种前馈人工神经网络&#xff0c;由多层节点组成&#xff0c;每层节点通过权重和偏置与下一层节点相连。MLP在许多领域都有广泛的应用&#xff0c;如分类、回归、自然语言处理等。 本文将分…

软考数据库---2.SQL语言

主要记忆&#xff1a;表、索引、视图操作语句&#xff1b;数据操作&#xff1b;通配符、转义符&#xff1b;授权&#xff1b;存储过程&#xff1b;触发器 这部分等等整理一下: “”" 1、 数据定义语言。 SQL DDL提供定义关系模式和视图、 删除关系和视图、 修改关系模式的…

基于ssm的大学生租房平台的设计与实现(java源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的大学生租房平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 大学生租房平台的设计与实现的主…

Python数据分析可视化之Pandas的使用

一、项目介绍 数据获取与存储&#xff1a;能够使用Python财经数据接口包tushare下载股票交易数据&#xff0c;并将数据保存到CSV文件或MySQL数据库中。数据处理&#xff1a;能够用Pandas从CSV文件、Excel文件以及MySQL数据库中读取数据。能够使用Pandas对数据进行简单处理和深…

某狗网歌曲接口逆向之加密算法刨析

逆向网址 aHR0cHM6Ly93d3cua3Vnb3UuY29t 逆向链接 aHR0cHM6Ly93d3cua3Vnb3UuY29tL21peHNvbmcvN2dxcGVzNjguaHRtbA 逆向接口 aHR0cHM6Ly93d3dhcGkua3Vnb3UuY29tL3BsYXkvc29uZ2luZm8 逆向过程 请求方式&#xff1a;GET 逆向参数 signature:1898d8f157837fadc9751fdacf1398f9 …

天猫精灵要会员,不能听歌,还能用来干什么呢?榨干它的剩余价值

目录 起因&#xff1a;以听歌为主要功能的设备&#xff0c;却不能听歌了 1.蓝牙音箱 2.控制智能家电 3.万能遥控器&#xff0c;需要一个外接设备 4.倒计时/提醒&#xff0c;闹钟提醒&#xff0c;整点提醒&#xff08;这功能有人不喜欢&#xff0c;闲吵&#xff0c;还不能关…

职场证件照:不只是一张照片那么简单,这些细节请注意

随着毕业季的到来&#xff0c;许多应届生已经开始在各自的岗位上实习&#xff0c;准备迎接转正的挑战。在这个过程中&#xff0c;一张得体的职场证件照将成为你职业生涯中的一张重要名片。在职场中&#xff0c;证件照的应用场景多种多样&#xff0c;从窗口岗位的公示到工作牌上…

Pytorch Windows EOFError: Ran out of input when num_workers>0

关于深度学习的一些学习框架,我使用过pytorch,caffe,caffe2,openchatkit,oneflow等,最近我将长达几十万字的报错手册重新进行了整理,制作出一个新的专栏,主要记录这几种常见的开发框架在安装和使用过程中常见的报错,以及我是如何解决掉的,以此来帮助更多的深度学习开…

紫光展锐T610平台_4G安卓核心板方案定制开发

紫光展锐T610核心板配备Android 11操作系统&#xff0c;采用12nm制程工艺。该处理器CPU由2颗基于Cortex-A75架构的大核心和6颗基于Cortex-A55架构的小核心组成&#xff0c;最高主频为1.8GHz。GPU采用的是614.4MHz的Mali G52&#xff0c;可以流畅播放2400*1080分辨率视频&#x…

maven之pom中的build标签

1、build标签分类 1.1、全局配置&#xff08;project build&#xff09; 针对整个项目的所有情况都有效。 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"htt…

知道智源开源最强语义向量模型BGE是什么吗?

Embedding模型作为大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;的一个重要辅助&#xff0c;是很多LLM应用必不可少的部分。但是&#xff0c;现实中开源的Emebdding模型却很少。北京智源人工智能研究院&#xff08;BAAI&#xff09;开源了BGE系列Emb…

Proxmox VE qm 方式恢复虚拟机

前言 使用qm 恢复Proxmox VE虚拟机&#xff0c;高效便捷。 登录Proxmox VE shell 执行恢复操作 假设备份好的文件在其它主机存储&#xff0c;我们可以下载到Proxmox VE本地目录下&#xff0c;如何执行虚拟化恢复操作--storage local-lvm&#xff08;恢复后存储到的位置&…

【linux】TCP编程{三次握手/四次挥手/API注意点/代码}

文章目录 1.API介绍1.1wc -l dirName1.2inet_pton1.3inet_aton1.4inet_ntop 2.三次握手与四次挥手1.三次握手2.四次挥手3.应用程序和TCP协议层如何交互总结 3.TCP 和 UDP 对比1.宏观2.详细 4.地址转换函数inet_ntoa 5.TCP编程代码Makefiletcp_client.cctcp_server.cctcp_server…

TypeScript学习--day1

一、介绍 TypeScript是JS的超集&#xff0c;为JS添加了类型支持。 1.1 为什么添加类型支持 JS代码的错误大部分是类型错误&#xff0c;增加改Bug时间&#xff0c;影响开发效率。 静态类型&#xff1a;编译期做类型检查 动态类型&#xff1a;执行期做类型检查 TS--静态类型编…

前端保留两位小数

一、保留两位小数&#xff08;四舍五入&#xff09; 解决方案&#xff1a;使用 toFixed(x) 方法可以对小数进行指定位数保留&#xff0c;其中x是要保留的位数用法&#xff1a;num.toFixed(x)&#xff0c;其中num为需要操作的数据&#xff0c;x为要保留的位数示例&#xff1a;1…

力扣LeetCode138. 复制带随机指针的链表 两种解法(C语言实现)

目录 题目链接 题目分析 题目定位&#xff1a; 解题思路 解题思路1&#xff08;粗暴但是复杂度高&#xff09; 解题思路2&#xff08;巧妙并且复杂度低&#xff09; 题目链接 138. 复制带随机指针的链表https://leetcode-cn.com/problems/copy-list-with-random-pointer/ …

双写一致性问题

双写一致性问题&#xff1a;同一份数据&#xff0c;需要写数据库、写缓存。数据库中的数据和缓存中的数据要一致 解决办法&#xff1a;延迟双删 当我们要进行更新操作时&#xff0c;先删除缓存&#xff0c;再更新数据库&#xff0c;延迟几百ms再删除一次redis的缓存数据。 示…

2023年蓝桥杯——日期统计

目录 题目链接&#xff1a;1.日期统计 - 蓝桥云课 (lanqiao.cn) 题目描述 思路 代码思路 定义数据结构&#xff1a; 处理每一个月&#xff1a; 检查日期序列在num100中是否存在&#xff1a; 计数匹配的日期数&#xff1a; 输出结果&#xff1a; 代码实现 总结 题目链…

【Python习题】某景区门票的优惠措施为:购买5张以内门票不打折,5到20张打九折,20张以上打八折。编写程序,根据购买的门票数量,输出总票价。

题干 某景区门票的优惠措施为:购买5张以内门票不打折&#xff0c;5到20张打九折&#xff0c;20张以上打八折。编写程序&#xff0c;根据购买的门票数量&#xff0c;输出总票价。 代码