3【鸿蒙/OpenHarmony/NDK】如何在鸿蒙应用中使用NDK?

发布于:2025-09-01 ⋅ 阅读:(19) ⋅ 点赞:(0)

各位码友们好!今天这篇干货主要聚焦实操细节,希望能帮大家少踩坑。​
要是过程中遇到哪块没看懂、有疑问,或者你有更优的实现思路,评论区尽管聊!发现文档里有疏漏或错误也尽管指出来 ——
技术这东西就得互相挑刺才能越磨越精,咱们一起把这些知识点吃透~

前置学习

Hellow World详细解读

我们从上到下来看下NDK是如何被引入到应用中的。

import { hilog } from '@kit.PerformanceAnalysisKit';
import testNapi from 'libentry.so';

const DOMAIN = 0x0000;

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize($r('app.float.page_text_font_size'))
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            this.message = 'Welcome';
            hilog.info(DOMAIN, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3));
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

在ets中调用通过NDK实现的js接口

我们来看,如何在ets侧调用通过NDK实现的js接口。

  • 首先import testNapi from 'libentry.so';这句是将我们js接口所在的so导进来,方便后续调用里面的接口。
  • testNapi.add(2, 3)这句就是调用我们通过NDK实现的js接口addadd方法的返回值是number类型,返回2加3的结果5。

如何调用NDK实现js接口的我们已经讨论清楚了。下面我们在进一步看下,DevEco是如何知道我们的libentry.so中到底包含了哪些方法的?

  • 如图1,我们在DevEco里面输入testNapi.的时候它能够自动联想出add方法,DevEco到底是如何知道libentry.so中有add方法的呢?
    图1自动联想:
    在这里插入图片描述
  • 如图2,我们发现有一个src/main/cpp/types/libentry/Index.d.ts文件,它里面声明了add方法。那么DevEco是怎么知道我们libentry.so中的方法声明是在src/main/cpp/types/libentry/Index.d.ts文件中的呢?
    图2方法声明文件:
    在这里插入图片描述
  • 如图3,在项目的配置文件oh-package.json5里面,声明了一个依赖,说我这个项目依赖libentry.so,和libentry.so相关的配置在./src/main/cpp/types/libentry目录下。
  • ./src/main/cpp/types/libentry目录下存在同名的一个配置文件oh-package.json5,里面定义了libentry.so的信息,其中就包括so对应的类型声明文件Index.d.ts
    图3项目配置文件:
    在这里插入图片描述
    总结一下,DevEco通过项目的配置文件oh-package.json5知道了这个项目依赖 ./src/main/cpp/types/libentry目录下的libentry.so。然后通过./src/main/cpp/types/libentry目录下的oh-package.json5配置文件知道了libentry.so相关的接口声明是放在Index.d.ts文件中的。这样DevEco读取Index.d.ts文件就能够知道libentry.so里面包含哪些接口了,就能够做自动联想了。

使用C++实现js接口

前面我们知道了如何在ets侧调用js接口,接下来我们看下被调用的add方法在C++侧是如何实现的?

#include "napi/native_api.h"

static napi_value Add(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    napi_value sum;
    napi_create_double(env, value0 + value1, &sum);

    return sum;

}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), 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 = "entry",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&demoModule);
}

注册napi模块

  • RegisterEntryModule函数作为so的构造函数,在so被加载成功后被调用。
  • 函数里面调用了napi_module_register方法,这是告诉系统说我这里有一个napi模块,模块信息在demoModule里面。
  • 通过demoModule的定义我们可以看到,这个模块的名字叫做entry,它的初始化函数是Init方法。
  • Init方法里面,首先定义了一个napi_property_descriptor类型的变量desc,里面有一个"add"字符串,这个代表js侧的方法名字。还有一个Add符号,这个就是定义在这个文件中的napi_value Add(napi_env env, napi_callback_info info)函数。这个意思是说,在调用js侧的add方法的时候,实际会调用到c++侧的napi_value Add(napi_env env, napi_callback_info info)方法。
  • 然后通过napi_define_properties将上述信息注册到js环境中。

解析js接口的参数

接着我们看C++侧的Add方法的实现,他是如何解析js侧传过来的参数,又是如何将计算结果传回到js侧的。

参数数解析

通过napi_get_cb_info获取JavaScript传入的参数信息:

  • 声明argc=2表示预期接收两个参数
  • args数组用于存储实际传入的参数值
  • 后两个nullptr表示不关心this对象和函数本身
类型检查

使用napi_typeof检查参数类型:

  • 分别对args[0]args[1]进行类型判断
  • 结果存储在valuetype0valuetype1变量中
数值转换

通过napi_get_value_double提取数值:

  • 将JavaScript的Number类型转换为C的double类型
  • 转换后的值存储在value0value1变量中
运算与返回

通过napi_create_double创建结果:

  • 将两个double值相加
  • 构造新的napi_value类型结果
  • 最终返回给JavaScript环境

网站公告

今日签到

点亮在社区的每一天
去签到