鸿蒙5.0开发进阶:NDK代码开发-Node-API使用指南(使用Node-API接口创建基本数据类型)

往期鸿蒙5.0全套实战文章必看:(文中附带全栈鸿蒙5.0学习资料)


使用Node-API接口创建基本数据类型

简介

ArkTS的Number类型是一个双精度64位二进制格式IEEE 754值。只有在-2^53+1到2^53-1范围内(闭区间)的整数才能在不丢失精度的情况下被表示,在超过该取值范围的情况下,需要使用BigInt对应的NPI接口来处理更大范围的整数。

基本概念

当使用Node-API接口进行数值类型的创建和获取时,有一些基本概念需要了解:

  • 数值类型 在使用Node-API接口时,可能需要从Node-API模块数值类型转换为ArkTS数值类型值,或者从ArkTS数据类型值转换为Node-API模块数值类型。在进行数据类型转换时,需要注意数据范围是否匹配,以及有无符号整数和双精度数值等区别。
  • 错误处理 在使用这些接口时,需要对可能发生的错误进行适当处理。比如,在创建整数值时可能发生内存分配错误或其他运行时错误,需要使用Node-API提供的错误处理机制来捕获并处理这些错误。
  • ArkTS交互 在开发过程中,需要考虑如何将创建的数值类型值与ArkTS环境进行交互,包括传递参数、返回值等。

场景和功能介绍

以下Node-API函数通常在开发ArkTS的Node-API模块时使用,以便处理数值类型值,帮助开发人员在Node-API模块中和JavaScrip数值进行交互:

接口描述
napi_get_value_uint32将ArkTS环境中number类型数据转为Node-API模块中的uint32类型数据。
napi_get_value_int32将ArkTS环境中获取的number类型数据转为Node-API模块中的int32类型数据。
napi_get_value_int64将ArkTS环境中获取的number类型数据转为Node-API模块中的int64类型数据。
napi_get_value_double将ArkTS环境中获取的number类型数据转为Node-API模块中的double类型数据。
napi_create_int32将Node-API模块中的int32_t类型转换为ArkTS环境中number类型。
napi_create_uint32将Node-API模块中的uint32_t类型转换为ArkTS环境中number类型。
napi_create_int64将Node-API模块中的int64_t类型转换为ArkTS环境中number类型。
napi_create_double将Node-API模块中的double类型转换为ArkTS环境中number类型。

使用示例

Node-API接口开发流程参考使用Node-API实现跨语言交互开发流程,本文仅对接口对应C++及ArkTS相关代码进行展示。

napi_get_value_uint32

用于从ArkTS环境中获取32位无符号整数值。

cpp部分代码

#include "napi/native_api.h"

static napi_value GetValueUint32(napi_env env, napi_callback_info info)
{
    // 获取传入的数字类型参数
    size_t argc = 1;
    napi_value argv[1] = {nullptr};
    // 解析传入的参数
    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);

    uint32_t number = 0;
    // 获取传入参数的值中的无符号32位整数
    napi_status status = napi_get_value_uint32(env, argv[0], &number);
    // 如果传递的参数不是数字,将会返回napi_number_expected,设置函数返回nullptr
    if (status == napi_number_expected) {
        return nullptr;
    }
    napi_value result = nullptr;
    // 创建传入参数无符号32位整数,并传出
    napi_create_uint32(env, number, &result);
    return result;
}

接口声明

// index.d.ts
export const getValueUint32: <T>(data: T) => number | void;

ArkTS侧示例代码

import hilog from '@ohos.hilog'
import testNapi from 'libentry.so'

let value = testNapi.getValueUint32<number>(111111111111);
let data = testNapi.getValueUint32<string>("sssss");
hilog.info(0x0000, 'Node-API', 'get_value_uint32_number %{public}d', value);
// 传入非数字"sssss"时函数返回undefined
hilog.info(0x0000, 'Node-API', 'get_value_uint32_number %{public}s', data);
// 传入uint32范围内的数字100时函数返回原数字
hilog.info(0x0000, 'Node-API', 'get_value_uint32_number %{public}d', testNapi.getValueUint32<number>(100));

napi_get_value_int32

将ArkTS value转为Node-API模块中的int32类型数据。

cpp部分代码

#include "napi/native_api.h"

static napi_value GetValueInt32(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    int32_t result32 = 0;
    // 解析传递的参数
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    // 将前端传过来的参数转为Node-API模块的int32类型
    napi_status status = napi_get_value_int32(env, args[0], &result32);
    // 如果传递的参数不是数字napi_get_value_int32接口将会返回napi_number_expected,设置函数返回nullptr
    if (status == napi_number_expected) {
        return nullptr;
    }
    // 调用napi_create_int32接口将int32类型的数据转为napi_value返回
    napi_value napiResult32 = nullptr;
    napi_create_int32(env, result32, &napiResult32);
    return napiResult32;
}

接口声明

// index.d.ts
export const getValueInt32: (value: number | string) => number | void;

ArkTS侧示例代码

import hilog from '@ohos.hilog'
import testNapi from 'libentry.so'

// 传入非数字“ss”时函数返回undefined
hilog.info(0x0000, 'Node-API', 'get_value_int32_not_number %{public}s', testNapi.getValueInt32('ss'));
// 传入int32范围内的数字100时函数返回原数字
hilog.info(0x0000, 'Node-API', 'get_value_int32_number %{public}d', testNapi.getValueInt32(100));
// 传入68719476735,此数字的二进制为111111111111111111111111111111111111,在int32类型中此二进制代表数字-1
hilog.info(0x0000, 'Node-API', 'get_value_int32_oversize %{public}d', testNapi.getValueInt32(68719476735));
// 大于2的31次-1的数字且不是二进制为111111111111111111111111111111111111这样的在int32中有特殊含义的数字也会溢出,导致数值发生改变,返回值按后32位二进制编码解码
hilog.info(0x0000, 'Node-API', 'get_value_int32_oversize %{public}d', testNapi.getValueInt32(687194767355));
// 传入NAN(not a number)、+Infinity(正无穷)或-Infinity(负无穷),会返回数字0
hilog.info(0x0000, 'Node-API', 'get_value_int32_number_NAN %{public}d', testNapi.getValueInt32(NaN));
hilog.info(0x0000, 'Node-API', 'get_value_int32_number_+Infinity %{public}d', testNapi.getValueInt32(+Infinity));
hilog.info(0x0000, 'Node-API', 'get_value_int32_number_-Infinity %{public}d', testNapi.getValueInt32(-Infinity));

napi_get_value_int64

将ArkTS value转为Node-API模块中的int64类型数据。

cpp部分代码

#include "napi/native_api.h"

static napi_value GetValueInt64(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    int64_t result64 = 0;
    // 解析传递的值
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    // 将前端传过来的参数转为Node-API模块的int64类型
    napi_status status = napi_get_value_int64(env, args[0], &result64);
    // 如果传递的参数不是数字, 返回napi_number_expected.
    if (status == napi_number_expected) {
        return nullptr;
    }
    // 调用napi_create_int64接口将int64类型的数据转为napi_value返回前端
    napi_value napiResult64 = nullptr;
    napi_create_int64(env, result64, &napiResult64);
    return napiResult64;
}

接口声明

// index.d.ts
export const getValueInt64: (value: number | string) => number | void;

ArkTS侧示例代码

import hilog from '@ohos.hilog'
import testNapi from 'libentry.so'

// 输入不超过int64表示范围的数字,会返回该数字
hilog.info(0x0000, 'Node-API', 'get_value_int64_number %{public}d', testNapi.getValueInt64(80));
// 传入非数字“ss”,获得函数返回的值应为undefined
hilog.info(0x0000, 'Node-API', 'get_value_int64_not_number %{public}s', testNapi.getValueInt64('sAs'));
// 输入超过int64表示范围的数字会溢出,失去精度,导致输入数字与返回数字不相等
hilog.info(0x0000, 'Node-API', 'get_value_int64_number_oversize %{public}d', testNapi.getValueInt64(9223372036854775809));
// 传入NAN(not a number)、+Infinity(正无穷)或-Infinity(负无穷)接口返回数字0
hilog.info(0x0000, 'Node-API', 'get_value_int64_number_NAN %{public}d', testNapi.getValueInt64(NaN));
hilog.info(0x0000, 'Node-API', 'get_value_int64_number_+Infinity %{public}d', testNapi.getValueInt64(+Infinity));
hilog.info(0x0000, 'Node-API', 'get_value_int64_number_-Infinity %{public}d', testNapi.getValueInt64(-Infinity));

napi_get_value_double

将ArkTS value转为Node-API模块中的double类型数据。

cpp部分代码

#include "napi/native_api.h"

static napi_value GetDouble(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
    double value = 0;
    napi_status status = napi_get_value_double(env, args[0], &value);
    // 传入非数字接口返回napi_number_expected
    if (status == napi_number_expected) {
        return nullptr;
    }
    napi_value result = nullptr;
    napi_create_double(env, value, &result);
    return result;
}

接口声明

// index.d.ts
export const getDouble: (value: number | string) => number | void;

ArkTS侧示例代码

import hilog from '@ohos.hilog'
import testNapi from 'libentry.so'
// 输入数字,返回该数字
hilog.info(0x0000, 'Node-API', 'get_value_double_number %{public}d', testNapi.getDouble(80.885));
// 传入非数字,获得函数返回的值应为undefined
hilog.info(0x0000, 'Node-API', 'get_value_double_not_number %{public}s', testNapi.getDouble('sAs'));

napi_create_int32

用于创建一个ArkTS数字(int32类型)的值。

cpp部分代码

#include "napi/native_api.h"
static constexpr int INT_NUM_NEG_26 = -26;   // int类型数值-26

static napi_value CreateInt32(napi_env env, napi_callback_info info)
{
    // int32_t是有符号的32位整数类型,表示带有符号的整数,它的范围是从-2^31到2^31 - 1,也就是-2147483648到2147483647
    // 要表示的整数值
    int32_t value = INT_NUM_NEG_26;
    // 创建ArkTS中的int32数字
    napi_value result = nullptr;
    napi_status status = napi_create_int32(env, value, &result);
    if (status != napi_ok) {
        // 处理错误
        napi_throw_error(env, nullptr, "Failed to create int32 value");
    }
    return result;
}

接口声明

// index.d.ts
export const createInt32: () => number;

ArkTS侧示例代码

import hilog from '@ohos.hilog'
import testNapi from 'libentry.so'

hilog.info(0x0000, 'testTag','Test Node-API napi_create_int32:' + testNapi.createInt32());

napi_create_uint32

用于创建一个ArkTS数字(uint32类型)的值。

cpp部分代码

#include "napi/native_api.h"
static constexpr int INT_NUM_26 = 26;   // int类型数值26

static napi_value CreateUInt32(napi_env env, napi_callback_info info)
{
    // 如果使用 uint32_t类型来定义-26,会发生溢出,溢出时会对结果进行模运算,将负数的二进制补码转换为相应的正数。-26输出4294967270
    // uint32_t是无符号的32位整数类型,只能表示非负整数。它的范围是从0到2 ^32 - 1,即0到4294967295
    // 要表示的整数值
    uint32_t value = INT_NUM_26;
    // 创建ArkTS中的uint32数字
    napi_value result = nullptr;
    napi_status status = napi_create_uint32(env, value, &result);
    if (status != napi_ok) {
        // 处理错误
        napi_throw_error(env, nullptr, "Failed to create uint32 value");
    }
    return result;
}

接口声明

// index.d.ts
export const createUInt32: () => number;

ArkTS侧示例代码

import hilog from '@ohos.hilog'
import testNapi from 'libentry.so'

hilog.info(0x0000, 'testTag','Test Node-API napi_create_uint32: ' + testNapi.createUInt32());

napi_create_int64

用于创建一个ArkTS数字(int64类型)的值。

cpp部分代码

#include "napi/native_api.h"

static napi_value CreateInt64(napi_env env, napi_callback_info info)
{
    // 要表示的整数值
    int64_t value = 2147483648;
    // 使用给定数值创建一个ArkTS number,仅能准确表示范围从-2^53 + 1到2^53 - 1(闭区间)的整数
    // 如果想表示的数值超过了2^53,请使用napi_create_bigint64接口
    napi_value result = nullptr;
    napi_status status = napi_create_int64(env, value, &result);
    if (status != napi_ok) {
        // 处理错误
        napi_throw_error(env, nullptr, "Failed to create int64 value");
    }
    return result;
}

接口声明

// index.d.ts
export const createInt64: () => number;

ArkTS侧示例代码

import hilog from '@ohos.hilog'
import testNapi from 'libentry.so'

hilog.info(0x0000, 'testTag','Test Node-API napi_create_int64: ' + testNapi.createInt64());

napi_create_double

用于创建一个ArkTS数字(double类型)的值。

cpp部分代码

#include "napi/native_api.h"

static napi_value CreateDouble(napi_env env, napi_callback_info info)
{
    double value = 1.234;
    // 创建ArkTS中的double数字
    napi_value result = nullptr;
    napi_status status = napi_create_double(env, value, &result);
    if (status != napi_ok) {
        // 处理错误
        napi_throw_error(env, nullptr, "Failed to create double value");
    }
    return result;
}

接口声明

// index.d.ts
export const createDouble: () => number;

ArkTS侧示例代码

import hilog from '@ohos.hilog'
import testNapi from 'libentry.so'

hilog.info(0x0000, 'testTag','Test Node-API napi_create_double: ' + testNapi.createDouble());

以上代码如果要在native cpp中打印日志,需在CMakeLists.txt文件中添加以下配置信息(并添加头文件:#include "hilog/log.h"):

// CMakeLists.txt
add_definitions( "-DLOG_DOMAIN=0xd0d0" )
add_definitions( "-DLOG_TAG=\"testTag\"" )
target_link_libraries(entry PUBLIC libhilog_ndk.z.so)
<think>根据用户需求,我们需要在鸿蒙开发使用C++获取应用程序自身的路径。结合提供的引用资料,特别是引用[4]中提到的NDK开发模板和CMake配置,我们可以推断需要使用Native API(如Node-API)来实现。 步骤分析: 1. 创建Native C++工程:使用DevEco Studio创建工程,选择“Native C++”模板(参考引用[1]和[3])。 2. 在C++代码中,我们需要调用系统API来获取应用程序的路径。在OpenHarmony中,可以通过Node-API调用系统服务或使用系统提供的Native API。 3. 根据OpenHarmony的文档,获取应用自身路径可以通过`ability`上下文获取。但在Native层,我们需要通过JS层传递上下文信息,或者使用Native API直接获取。 然而,查阅现有资料,并没有直接提到在C++层获取应用路径的API。因此,我们需要考虑以下两种方式: 方式一:在JS/TS层获取应用路径,然后通过Node-API传递给C++层。 方式二:在C++层直接调用系统底层的API(如果有的话)。 由于OpenHarmony的Node-API目前可能不提供直接获取应用路径的接口,我们可能需要通过JS层传递。 参考引用[3]中提到的“ArkTS/JS与C/C++混合开发”,我们可以: 1. 在JS层获取应用上下文,进而得到应用路径。 2. 将路径通过Node-API传递给C++。 具体步骤: 步骤1:在JS层获取应用路径 在OpenHarmony中,应用路径可以通过`Context`对象获取。例如,在Ability中: ```js let context = ...; // 获取Ability上下文 let bundleName = context.bundleName; // 获取包名 let filesDir = context.filesDir; // 获取应用文件路径 ``` 但是,我们可能需要的是应用程序的安装路径,通常为`/data/app/el1/bundle/public/<包名>/`。 步骤2:通过Node-API将路径传递给C++ 在C++代码中,我们可以定义一个Native方法,该方法接收一个字符串参数(即路径)。 步骤3:在C++中接收并使用该路径。 实现示例: 1. 在JS层定义Native方法,并调用: 在`entry/src/main/ets/pages/Index.ets`中: ```js import nativeManager from 'libentry.so'; // 导入native模块 @Entry @Component struct Index { private path: string = ''; // 用于存储路径 aboutToAppear() { let context = getContext(this); // 获取当前上下文 // 获取应用文件路径,这里使用filesDir作为示例,实际可能需要其他路径 this.path = context.filesDir; // 调用native方法,将路径传递给C++ nativeManager.setAppPath(this.path); } build() { // ... } } ``` 2. 在C++层定义对应的Native方法: 在`entry/src/main/cpp/hello.cpp`中: ```cpp #include "napi/native_api.h" #include <string> // 全局变量存储路径 static std::string appPath = ""; // 定义Native方法,供JS调用 static napi_value SetAppPath(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value args[1]; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); // 从JS参数中获取字符串 size_t length; napi_get_value_string_utf8(env, args[0], nullptr, 0, &length); char* buffer = new char[length + 1]; napi_get_value_string_utf8(env, args[0], buffer, length + 1, &length); appPath = std::string(buffer); delete[] buffer; // 返回undefined napi_value result; napi_get_undefined(env, &result); return result; } // 定义模块导出 static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { { "setAppPath", nullptr, SetAppPath, nullptr, nullptr, nullptr, napi_default, nullptr } }; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); return exports; } // 注册模块 NAPI_MODULE(example, Init) ``` 3. 在C++的其他地方就可以使用`appPath`这个全局变量了。 但是,这种方法有一个缺点:全局变量`appPath`的赋值依赖于JS的调用时机。如果C++代码在JS调用之前使用这个变量,就会出错。 另一种更安全的方式是:在需要路径的时候,从JS层同步调用Native方法获取路径(即每次需要路径时都从JS层获取,而不是存储全局变量)。但这样会增加JS与Native的交互次数。 或者,我们可以在JS层获取路径后,将其存储在C++的一个全局变量中,并确保在C++使用路径之前已经设置好。这需要控制好调用顺序。 注意:上述示例中使用了`filesDir`,即应用内部存储路径。如果需要安装路径,OpenHarmony的Context提供了`bundleCodeDir`属性,它返回应用程序的安装路径(只读)。 因此,在JS层可以这样获取安装路径: ```js let context = getContext(this); let installPath = context.bundleCodeDir; ``` 然后传递给C++。 如果希望在C++层直接获取而不依赖JS,目前OpenHarmony的NDK没有提供直接获取应用路径的API。所以上述方法是一个可行的方案。 总结: 在鸿蒙开发中,使用C++获取应用程序自身的路径,可以通过以下步骤实现: 1. 在JS/TS层使用Context获取应用路径(如`bundleCodeDir`或`filesDir`)。 2. 通过Node-API将路径作为字符串传递给C++模块。 注意:在C++中,需要确保在JS层已经调用了设置路径的方法之后,再使用该路径。 相关问题: 1. 在OpenHarmony中,如何获取应用的各种路径(如安装路径、文件路径等)? 2. Node-API如何实现JS与C++之间的字符串传递? 3. 在Native C++开发中,如何避免全局变量在多线程环境下的竞争问题?</think>在鸿蒙开发中,使用 C++ 获取应用程序自身路径需要结合 Node-API鸿蒙系统 API 实现。以下是详细步骤(基于 DevEco Studio 5.0): ### 实现步骤 1. **创建 Native C++ 工程** 在 DevEco Studio 中选择 "Native C++" 模板创建工程[^1][^3]。 2. **修改 CMakeLists.txt** 在 `entry/src/main/cpp/CMakeLists.txt` 中添加 Node-API 依赖: ```cmake target_link_libraries(entry PUBLIC libace_napi.z.so libbundle_framework.z.so) ``` 3. **C++ 层代码实现** 在 `entry/src/main/cpp/hello.cpp` 中添加: ```cpp #include "napi/native_api.h" #include "bundle/bundle_info.h" #include "bundle/bundle_manager.h" // 获取应用路径的Native方法 static napi_value GetAppPath(napi_env env, napi_callback_info info) { // 获取BundleManager实例 auto bundleMgr = GetBundleManager(); if (bundleMgr == nullptr) { napi_throw_error(env, nullptr, "BundleManager is null"); return nullptr; } // 获取当前应用BundleName char bundleName[128]; size_t len = 128; napi_get_named_property(env, info, "bundleName", &bundleName[0], &len); // 获取应用安装路径 char path[256]; int ret = bundleMgr->GetBundleCodePath(bundleName, path, sizeof(path)); if (ret != 0) { napi_throw_error(env, nullptr, "Get path failed"); return nullptr; } // 返回路径字符串 napi_value result; napi_create_string_utf8(env, path, NAPI_AUTO_LENGTH, &result); return result; } // 注册Native模块 napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc = { "getAppPath", nullptr, GetAppPath, nullptr, nullptr, nullptr, napi_default, nullptr }; napi_define_properties(env, exports, 1, &desc); return exports; } NAPI_MODULE(apppath, Init) ``` 4. **ArkTS 层调用** 在 `entry/src/main/ets/pages/Index.ets` 中: ```typescript import apppath from 'libentry.so' // 导入Native模块 @Entry @Component struct Index { @State path: string = "" aboutToAppear() { this.path = apppath.getAppPath() // 调用Native方法 } build() { Column() { Text(`应用路径: ${this.path}`) .fontSize(20) .margin(20) } } } ``` ### 关键说明 1. **权限配置** 在 `module.json5` 中添加权限声明: ```json "requestPermissions": [ { "name": "ohos.permission.GET_BUNDLE_INFO" } ] ``` 2. **路径类型** 根据需求可替换以下系统 API- `GetBundleCodePath()`:安装目录(如 `/data/app/...`) - `GetBundleDataPath()`:数据目录(如 `/data/storage/...`) 3. **Node-API 规范** 需遵循 OpenHarmony 的 [Node-API 规范](https://gitee.com/openharmony/arkui_napi) 实现跨语言调用[^3]。 ### 注意事项 1. 确保 SDK 版本 ≥ 5.0,并在 `build-profile.json5` 中配置: ```json "buildOption": { "externalNativeOptions": { "path": "./src/main/cpp/CMakeLists.txt" } } ``` 2. 首次运行需在设备上授权 `GET_BUNDLE_INFO` 权限 3. 调试时可通过 `hilog` 输出路径信息: ```cpp #include "hilog/log.h" OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "PATH", "App path: %{public}s", path); ``` --- ### 相关问题 1. 如何在鸿蒙 Native 开发中读写应用私有文件? 2. Node-API 如何实现 JS 与 C++ 间的复杂数据结构传递? 3. 鸿蒙应用如何获取其他应用的安装路径? 4. 在 Native 层如何监听鸿蒙应用的生命周期变化? [^1]: 参考 DevEco Studio 创建 Native C++ 应用的工程配置 [^2]: 鸿蒙 SDK 集成路径 `/DevEco Studio/sdk` [^3]: Node-API 实现 ArkTS 与 C++ 交互的规范 [^4]: NDK 工程中 CMake 的三方库链接方法
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值