HarmonyOS ArkUI实战开发-NAPI项目

747 篇文章 5 订阅
606 篇文章 10 订阅

上节笔者简单介绍了使用 DevEco Studio 创建的默认 NAPI 工程结构,本节笔者简单介绍一下 NAPI 工程下 cpp 目录的源码部分。

index.d.ts解读

在 cpp 的 libentry 目录下生成了 index.d.ts 文件,它的源码如下所示:

export const add: (a: number, b: number) => number;

export const 表示导出一个常量以便在其它文件中使用。add 是一个返回类型为 number 的方法,它的参数类为 number 类型。

package.json解读

在 cpp 的 libentry 目录下生成了 package.json 文件,该文件是打包的配置文件,内容如下所示:

{
  "name": "libentry.so",
  "types": "./index.d.ts"
}

设置 libentry.so 库和 index.d.ts 相关联,便于在 TS 文件中引入 libentry.so 时调用库中的相关方法。

CMakeLists.txt解读

CMake 是一个开源跨平台的构建工具,旨在构建、测试和打包软件,CMake 是 makefile 的上层工具,用于跨平台构建环境,生成可移植的 makefile 并简化自动动手写 makefile 的工作量,在 cpp 目录下默认生成的 CMakeLists.txt 内容如下所示:

# the minimum version of CMake.
# 声明使用 CMAKE 的最小版本号
cmake_minimum_required(VERSION 3.4.1)

# 声明项目的名称
project(oh_0400_napi)

# set命令,格式为set(key value),表示设置key的值为value,其中value可以是路径,也可以是许多文件。
# 本例中设置NATIVERENDER_ROOT_PATH的值为${CMAKE_CURRENT_SOURCE_DIR}
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

# 添加项目编译所需要的头文件的目录
include_directories(${NATIVERENDER_ROOT_PATH}
                    ${NATIVERENDER_ROOT_PATH}/include)

# 生成目标库文件libentry.so,entry表示最终的库名称,SHARED表示生成的是动态链接库,
# hello.cpp表示最终生成的libentry.so中所包含的源码
# 如果要生成静态链接库,把SHARED该成STATIC即可
add_library(entry SHARED hello.cpp)

# 把libentry.so链接到libace_napi.z.so上
target_link_libraries(entry PUBLIC libace_napi.z.so)

CMakeLists.txt 内容注释比较详细,笔者就不一一叙述了,更多详细用法读者可自行查阅官网。

hello.cpp解读

在 cpp 目录下默认生成的 hello.cpp 文件,源码如下所示:

#include "napi/native_api.h"
#include <js_native_api.h>
#include <js_native_api_types.h>

static napi_value Add(napi_env env, napi_callback_info info)
{
    size_t requireArgc = 2;
    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);
}

hello.cpp 的代码不是很复杂,笔者把它做如下拆分:

  • 引入头文件
    #include "napi/native_api.h"
    #include <js_native_api.h>
    #include <js_native_api_types.h>

引入头文件,作用和 TS 里的 import 类似,不再详述。

  • 注册napi模块
    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 模块,类型为 napi_module 结构体,各字段说明如下:

*   **nm_version**:nm版本号,默认值为 1。
*   **nm_flags**:nm标记符,默认值为 0。
*   **nm_filename**:暂不关注,使用默认值即可。
*   **nm_register_func**:指定nm的入口函数。
*   **nm_modname**:指定 TS 页面导入的模块名,例如:`import testNapi from 'libentry.so'` 中的 testNapi 就是当前的nm_modname。
*   **nm_priv**:暂不关注,使用默认值即可。
*   **reserved**:暂不关注,使用默认值即可。

extern "C" 简单理解就是告诉编译器这部分代码按照 C 语言进行编译而不是 C++ 语言编译。__attribute__((constructor)) 声明方法的执行时机,它表示 RegisterEntryModule() 方法在 main() 方法执行前执行,简单理解就是当前 CPP 文件被编译成动态链接库 so 后,在调用 dlopen() 方法加载该库时会先执行 RegisterEntryModule() 方法。该方法内又调用了 napi_module_register() 方法,napi_module_register() 方法是 NAPI 提供的模块注册方法,表示把定义的 demoModule 模块注册到 JS 引擎中。

  • 方法定义
    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

Init() 方法内声明了 napi_property_descriptor 结构体,结构体的定义看第一个和第三个参数即可,第一个参数 add 表示应用层 JS 声明的方法,Add 表示 C++ 实现的方法,然后调用 NAPI 的 napi_define_properties() 方法将 add 和 Add 这俩方法做做个映射,最后通过 exports 变量对外导出,实现 JS 端调用 add 方法时进而调用到 C++ 的 Add() 方法。

  • 方法实现
    static napi_value Add(napi_env env, napi_callback_info info)
    {
        // 获取 2 个参数,napi_value是对 JS 类型的封装
        size_t requireArgc = 2;
        size_t argc = 2;
        napi_value args[2] = {nullptr};
        // 调用napi_get_cb_info方法,从 info 中读取传递进来的参数放入args里
        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);

        // 调用napi_get_value_double把 napi_value 类型转换成 C++ 的 double 类型
        double value0;
        napi_get_value_double(env, args[0], &value0);
        double value1;
        napi_get_value_double(env, args[1], &value1);

        // 调用napi_create_double方法把 C++类型转换成 napi_value 类型
        napi_value sum;
        napi_create_double(env, value0 + value1, &sum);

        // 返回 napi_value 类型
        return sum;

    }

Add() 方法注释的很清楚,首先从 napi_callback_info 中读取 napi_value 类型的参数放入到 args 中,然后从 args 中读取参数并把 napi_value 类型转换成 C++ 类型后进行加操作,最后把相加的结果转换成 napi_value 类型并返回。

  • 模块导入
    import testNapi from 'libentry.so'

根据前边的编译配置,cpp 目录下的源码最终打包成了 libentry.so,使用前直接引入即可。

  • 方法调用
    import testNapi from 'libentry.so'

    @Entry @Component struct Index {

      @State message: string = 'Hello,OpenHarmony'

      build() {
        Row() {
          Column() {
            Text(this.message)
              .fontSize(25)
              .fontWeight(FontWeight.Bold)
              .onClick(() => {
                var result = testNapi.add(2, 3);
                this.message = "Hello,OpenHarmony, value: " + result;
                console.log(this.message);
              })
          }
          .width('100%')
        }
        .height('100%')
      }
    }

引入 libentry.so 模块后,就可以直接调用 add() 方法了。

小结

本节简单介绍了默认创建的 NAPI 工程源码,读者有个大致印象即可,下节笔者将简单介绍一下 NAPI 提供的相关方法,便于后续编写 C++ 代码或者移植三方库。

码牛课堂也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线。大家可以进行参考学习:https://qr21.cn/FV7h05

①全方位,更合理的学习路径
路线图包括ArkTS基础语法、鸿蒙应用APP开发、鸿蒙能力集APP开发、次开发多端部署开发、物联网开发等九大模块,六大实战项目贯穿始终,由浅入深,层层递进,深入理解鸿蒙开发原理!

②多层次,更多的鸿蒙原生应用
路线图将包含完全基于鸿蒙内核开发的应用,比如一次开发多端部署、自由流转、元服务、端云一体化等,多方位的学习内容让学生能够高效掌握鸿蒙开发,少走弯路,真正理解并应用鸿蒙的核心技术和理念。

③实战化,更贴合企业需求的技术点
学习路线图中的每一个技术点都能够紧贴企业需求,经过多次真实实践,每一个知识点、每一个项目,都是码牛课堂鸿蒙研发团队精心打磨和深度解析的成果,注重对学生的细致教学,每一步都确保学生能够真正理解和掌握。

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:https://qr21.cn/FV7h05

如何快速入门:

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr21.cn/FV7h05

大厂鸿蒙面试题::https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值