0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

NAPI 类对象导出及其生命周期管理(下)

OpenHarmony开发经验 来源:OpenHarmony开发经验 作者:OpenHarmony开发经验 2023-05-16 10:25 次阅读

往期回顾:NAPI 类对象导出及其生命周期管理(上)

4. 样例工程源码剖析

  • 工程的模板是Native C++,模型是Stage

  • 源码剖析主要围绕以下几个文件

    image.png

4.1. NAPI导出对象和生命周期管理具体实现

4.1.1. 定义NapiTest类及方法

  • Napi.h文件内容如下:
#ifndef __NAPI_TEST_H__
#define __NAPI_TEST_H__

#include "napi/native_api.h"
#include 

#include 

#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值的生命周期,包括显式地定义它们的最小生命周期。

    https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_ref

4.1.1.3 napi_env

  • napi_env用于表示上下文,底层的Node-API实现可以使用该上下文持久保持VM-specific的状态。

    https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_env

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

参考 https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_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其中一个必须是NULL
  const 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函数说明:

https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_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)

image.png

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::Destructo方法是用来释放创建的对象:
void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize)
{
    // 类析构函数,释放资源
    NapiTest *test = reinterpret_cast
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类

// 创建生命周期,初始引用计数设为1
    if (napi_create_reference(env, constructor, 1, &sConstructor_) != napi_ok) {
        return nullptr;
    }

    // 设置NapiTest对象相关属性并绑定到导出变量exports
    if (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

https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_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到导出的对象result
    if (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

https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_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

https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_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

https://gitee.com/openharmony/napi_generator/blob/master/docs/INSTRUCTION_ZH.md

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
@Component

struct Index {
  @State message: string = '导出对象'
  @State nativePointer:number = 0

// 创建对象tt
  tt = 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打印,在程序中添加 log
                console.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%')
  }

}

知识点附送

napi接口名称

https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/native-lib/third_party_napi/napi.md

NAPI框架api调用值得借鉴的示例工程

https://gitee.com/jiangtao92/node-api-test-case

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 源码
    +关注

    关注

    8

    文章

    632

    浏览量

    29092
  • 类对象
    +关注

    关注

    0

    文章

    3

    浏览量

    1444
  • OpenHarmony
    +关注

    关注

    25

    文章

    3627

    浏览量

    16027
收藏 人收藏

    评论

    相关推荐

    基于Rust语言中的生命周期

    Rust是一门系统级编程语言具备高效、安和并发等特,而生命周期是这门语言中比较重要的概念之一。在这篇教程中,我们会了解什么是命周期、为什么需要生命周期、如何使用生命周期,同时我们依然会
    的头像 发表于 09-19 17:03 848次阅读

    AutoScaling 生命周期挂钩功能

    摘要: AutoScaling 伸缩组实例管理功能全面升级,新上线生命周期挂钩(LifecycleHook)功能,方便用户更加灵活地管理伸缩组内实例。使用生命周期挂钩可以在伸缩组发生伸
    发表于 06-27 17:13

    理解数据生命周期管理思路

    数据生命周期管理的思考
    发表于 03-17 10:49

    HarmonyOS应用开发-PageAbility生命周期

    pageAbility的生命周期如下图所示:在代码中通过调用下列方法实现生命周期操作:onShow() :Ability由后台不可见状态切换到前台可见状态调用onShow方法,此时用户在屏幕可以看到
    发表于 10-17 11:11

    触觉智能RK3568使用体验—NAPI 对象导出及其生命周期管理(上)

    本篇知识点,下半部分将对源码进行解析。通过本文您将熟悉:NAPI 导出对象NAPI 对象生命周期
    发表于 02-08 17:10

    在S32G2 RM中有“生命周期”,生命周期的完整含义是什么?

    在S32G2 RM中,有“生命周期”。生命周期的完整含义是什么,我们应该如何使用它?
    发表于 04-23 10:37

    物联网将会对产品生命周期管理产生哪些影响

    根据研究,到2020年,连网设备的数量预计将达到750亿台。在许多方面,物联网还将通过其积极的“多米诺骨牌效应”从根本上影响产品生命周期管理,这将有助于在产品的整个生命周期内提高产品的有效性。
    发表于 03-13 08:47 812次阅读

    存储&作用域&生命周期&链接属性

    目录前言一、存储&作用域&生命周期&链接属性的概念解析二、linuxC程序 的内存映像三、存储相关的关键字四、作用域详解五、变量的
    发表于 12-09 15:51 5次下载
    存储<b class='flag-5'>类</b>&作用域&<b class='flag-5'>生命周期</b>&链接属性

    Synopsys 启动硅生命周期管理计划

    Synopsis 的数据分析驱动的硅生命周期管理计划解决了 IC 生命周期中的质量、可靠性和安全挑战。
    发表于 08-18 15:37 804次阅读
    Synopsys 启动硅<b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>计划

    Elasticsearch索引生命周期常见的阶段

    在 Elastic Stack 6.6 版本后推出了新功能 Index Lifecycle Management(索引生命周期管理),支持针对索引的全生命周期托管管理,并且在 Kiba
    的头像 发表于 10-13 10:38 1180次阅读

    生命周期管理:COTS视角

    全面的生命周期管理策略是保护程序和缓解与长期任务关键型系统中部署的 COTS 技术相关的挑战的关键。除了降低风险外,生命周期管理服务还通过确保及时购买和储存报废 (EOL) 组件并大大
    的头像 发表于 11-08 14:18 1031次阅读
    <b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>:COTS视角

    Vue入门Vue的生命周期

    .生命周期 4.1生命周期是什么 Vue的生命周期, 就是Vue实例从创建到销毁的过程.
    的头像 发表于 02-06 16:16 827次阅读
    Vue入门Vue的<b class='flag-5'>生命周期</b>

    触觉智能RK3568使用体验:NAPI 对象导出及其生命周期管理(上)

    写在开头: OpenHarmony 中的  N-API组件定义了由ArkTS (JS/ETS)语言编写的代码和 native 代码(使用 C/C++ 编写)交互的方式,由 Node.js Node-API 框架扩展而来。 什么是Node.js Node-API 框架 Node.js Node-API为开发者提供了一套C/C++ API用于开发Node.js的Native扩展模块。从Node.js 8.0.0开始,Node-API以实验性特性作为Node.js本身的一部分被引入,并且从Node.js 10.0.0开始正式全面支持Node-API。 本文以OpenHarmony 3.2beta3源码中的node.js三方库为例,从third_party/node/README.OpenSource中可得知3.2bet
    的头像 发表于 02-17 09:10 833次阅读

    从设计到部署:芯片生命周期管理如何优化整个IC生命周期

    SLM涉及半导体生态系统的所有阶段。它旨在收集有关芯片及其操作的大量数据,并使用复杂的分析来优化芯片及其整个生命周期部署系统的性能、安全性和效率。该技术可用于提高设计鲁棒性、减少设计裕量、提高电源
    的头像 发表于 05-24 18:03 2530次阅读

    什么是设备全生命周期管理系统?

    设备全生命周期管理系统是一款能够对设备进行全周期数字化管理的软件平台,它通过将设备信息电子化,使得设备的管理和监督更加便捷。这个系统不仅涵盖
    的头像 发表于 02-26 14:21 1054次阅读
    什么是设备全<b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>系统?