乐趣区

关于openharmony:触觉智能RK3568使用体验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 值的生命周期,包含显式地定义它们的最小生命周期。
    https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_nap…
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_nap…

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_nap…

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_nap…

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 类

    // 创立生命周期,初始援用计数设为 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_nap…

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_nap…

函数阐明:

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_nap…

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…

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/applicat…

退出移动版