ArkTs之NAPI学习

1.Node-api组成架构

为了应对日常开发经的网络通信、串口访问、多媒体解码、传感器数据收集等模块,这些模块大多数是使用c++接口实现的,arkts侧如果想使用这些能力,就需要使用node-api这样一套接口去桥接c++代码。Node-api整体的架构图如下:

Node-api接口是基于node.js的一个扩展,所以在平常开发过程也可以参照node.js官网,像接口实现的功能,入参都是类似的。

Framework模块介绍

1)ModuleManager是管理对象的模块,当arkts侧调用c++时,会加载Native侧的模块到ModuleMangger,并转化为arkts对象返回上层。

2)ScopeManager:用于管理napi_value生命周期,napi_value是Node-api独特的数据类型,类似于ArkTs中的number、String等各种参数类型的统一表示形式,在Native侧代码开发中不需要感知不同类型的数据类型,统一都是napi_value。

3)ReferenceManager:用于管理引用,开发时经常会有一些跨线程的场景,这个时候就需要创建引用(napi_ref),否则就就会被GC回收掉。napi_ref用于指向napi_value,允许用户管理napi_value值的生命周期。

3)Native Engine的作用是统一ArkTS引擎在Node-API层的接口行为。

4)方舟运行时,也就是ArkTS引擎,整个Node-API模块都是跑在方舟运行时的。

2.Napi和native的交互流程

主要分为两步:模块初始化和函数调用

模块初始化

ArkTS在import一个so库的时候,会先找到ArkTS引擎,ArkTS引擎会加载模块信息到ModuleMananger,其实就是对应的dlopen函数(首次调用时加载,多次调用回去缓存查找)。之后ModuleManager会把模块信息返回给ArkTS引擎。ArkTS引擎拿到模块信息后,在native层触发模块注册,来初始化模块。注册完成之后通过底层框架返回给上层一个ArkTS对象,这个对象上挂在着c/c++侧的方法。我们拿到arkts对象后,就可以去调用c/c++的方法了。

函数调用

当arkts侧通过上述import返回的对象的调用方法时,ArkTS引擎会找到并调用对应的C/C++方法

3.Node-API支持的数据类型

napi_status:枚举数据类型,表示Node-API接口返回的状态信息。每当调用一个Node-API函数,都会返回该值,表示操作成功与否的相关信息。枚举信息如下:

typedef enum{
    napi_ok,
    napi_invalid_arg,
    napi_object_expected,
    napi_string_expected,
    napi_name_expected,
    ...
    napi_would_deadlock
} napi_status;

if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
    return nullptr;
}

Node-API将基本数据类型公开为各种API使用的抽象。这些API应该被视为不透明的,只能通过其他Node-API调用进行自省。

  • napi_value:在Native侧代码开发中不需要感知不同类型的数据,统一都是napi_value
    举例说明:napi_value napiResult; napi_create_double(env, 2.0, &napiResult);
  • napi_env:Native侧函数入参,表示Napi-API执行时的上下文。退出native侧的时,napi_env将失效
  • napi_callback_info: Native侧函数的入参,保存了ArkTS侧的参数信息,用于传递给napi_get_cb_info()函数获取ArkTS侧入参信息。
    举例说明:
    static napi_value MyHypot(napi_env env, napi_callback_info info)
    {
        size_t argc = 2;
        napi_value args[2] = {nullptr};
        // 从info中获取参数信息到参数数组args[]
        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    }

    4.Node-API接口

Node-API接口在Node.js提供的原生模块基础上,目前只支持部分接口。常用的部分接口介绍如下:

  • napi_get_cb_info: 从给定的napi_callback_info中获取有关调用的详细信息,如参数和this指针
    static napi_value MyHypot(napi_env env, napi_callback_info info)
    {
        size_t argc = 2;
        napi_value args[2] = {nullptr};
        // 从info中获取参数信息到参数数组args[]
        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    }
  • napi_get_value_double: 获取给定ArkTS的number类型
    double valueX = 0.0;
    //获取args[0]存储的参数信息并赋值给valueX,args[]中存储接受ArkTs侧参数
    napi_get_value_double(env, args[0], &valueX); 

  • napi_create_string_utf8: 通过utf8编码的c字符串数据创建ArtTS侧String类型的数据

    std::string str = "temp";
    napi_value sum; //用于存储转换类型后的string类型数据
    napi_create_string_utf8(env, str.c_str(), str.length(). &sum);

    5.案例讲解说明交互流程

目录说明

CPP目录

        types/index.d.ts:定义了c++侧需要暴漏在ArkTS侧的接口

        oh-package.json5是描述index.d.ts文件的

        napi_init.cpp文件在里面做Native模块的注册、类型的转换以及大部分业务逻辑

ets目录

page/index.ets 里面有ArkTS调用C++

src目录

build-profile.json5:主要是配置构建信息(主要是cmakelist的构建路径,构建的时候能够找到cmakelist文件)

oh-package.json5:主要配置了一些模块本身的信息和依赖的工程

6.官方代码案例


基于Node-API开发业务功能

static napi_value MyHypot(napi_env env, napi_callback_info info)
{
    if ((nullptr == env) || (nullptr == info)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot", "env or exports is null");
        return nullptr;
    }
    // Number of parameters.
    size_t argc = 2;
    // Declare parameter array.
    napi_value args[2] = { nullptr };
    // Gets the arguments passed in and puts them in the argument array.
    if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot", "api_get_cb_info failed");
        return nullptr;
    }
    // Converts arguments passed in to type double.
    double valueX = 0.0;
    double valueY = 0.0;
    if (napi_ok != napi_get_value_double(env, args[0], &valueX) ||
        napi_ok != napi_get_value_double(env, args[1], &valueY)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot", "napi_get_value_double failed");
        return nullptr;
    }
    // The hypot method of the C standard library is called to perform the calculation.
    double result = hypot(valueX, valueY);
    napi_value napiResult;
    if (napi_ok != napi_create_double(env, result, &napiResult)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot", "napi_create_double failed");
        return nullptr;
    }
    return napiResult;
}


接口映射

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    if ((nullptr == env) || (nullptr == exports)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null");
        return exports;
    }
    napi_property_descriptor desc[] = {
        { "myHypot", nullptr, MyHypot, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    if (napi_ok != napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
        return nullptr;
    }
    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 }
};
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
    napi_module_register(&demoModule);
}


模块构建配置

cmakelist.txt文件:

# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(NativeTemplateDemo)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(
    ${NATIVERENDER_ROOT_PATH}
    ${NATIVERENDER_ROOT_PATH}/include
)
find_library(
    # Sets the name of the path variable.
    hilog-lib
    # Specifies the name of the NDK library that
    # you want CMake to locate.
    hilog_ndk.z
)
add_library(hello SHARED hello.cpp)
target_link_libraries(hello PUBLIC ${hilog-lib} libace_napi.z.so libc++.a)

根目录下的build-profile.json5

{
  "apiType": 'stageMode',
  "buildOption": {
    "externalNativeOptions": {
      "path": "./src/main/cpp/CMakeLists.txt",
      "arguments": "",
      "abiFilters": [
        "arm64-v8a",
        "x86_64"
      ],
      "cppFlags": "",
    }
  },
  "targets": [
    {
      "name": "default",
      "runtimeOS": "HarmonyOS"
    }
  ]
}

导出native接口

/entry/oh-package.json5

{
  "license": "",
  "devDependencies": {
    "libhello.so": "file:./src/main/cpp/types/libhello"
  },
  "author": "",
  "name": "entry",
  "description": "Please describe the basic information.",
  "main": "",
  "version": "1.0.0",
  "dependencies": {}
}

7.更多接口说明参照官网

华为开发者学堂

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值