当前位置:   article > 正文

三方库移植之NAPI开发[1]—Hello OpenHarmony NAPI_openharmony napi 引用第三方库

openharmony napi 引用第三方库

通过本文您将熟悉:

  • 如何注册NAPI模块及接口。
  • 如何在ArkUI eTS代码中调用扩展的NAPI接口。
  • full-SDK的替换。

什么是NAPI

  • NAPI(Native API)组件是一套对外接口基于Node.js N-API规范开发的原生模块扩展开发框架。

  • OpenHarmony中组件有一种是C和C++语言的三方组件,通常以源码或OpenHarmony hpm包的方式引入,在应用开发中以NAPI的方式使用,或直接编译在OpenHarmony操作系统镜像

NAPI组件架构图

  • OpenHarmony 标准系统应用开发基于ArkUI框架,开发语言使用JS/eTS。部分业务场景依赖使用现有的C/C++ 库,或为了获取更高的性能。OpenHarmony提供NAPI机制,用于规范封装IO、CPU密集型、OS底层等能力并对外暴露JS接口,通过NAPI实现JS和C/C++代码的互相访问.

    • 重点讲解了NAPI接口如何实现OpenCV以及SeetaFace的调用。一句话概括就是,钟禄平和林嘉诚老师讲述了移植了三方库后通过NAPI将库的C/C++接口变成JS/ETS接口给应用层调用。
  • OpenHarmony 中的 N-API 定义了由 JS/ETS 语言编写的代码和 native 代码(使用 C/C++ 编写)交互的方式,由 Node.js N-API 框架扩展而来。

  • N-API:Native Application Programming Interface(本地应用程序接接口)
  • 什么是Node.js N-API 框架
    Node.js N-API为开发者提供了一套C/C++ API用于开发Node.js的Native扩展模块。从Node.js 8.0.0开始,N-API以实验性特性作为Node.js本身的一部分被引入,并且从Node.js 10.0.0开始正式全面支持N-API。

添加OpenHarmony自定义子系统、组件、模块

  • 这部分内容涉及三方库移植,为便于本篇NAPI基础的学习。笔者在此自定义一个子系统用于开发NAPI。如在已存在的子系统组件中添加扩展NAPI,则跳过此步。
  • 需要准备好OpenHarmonyBeta3源码和编译环境

添加子系统、组件

直接在OpenHarmony源码根目录创建子系统文件夹,取名mysubsys。并在目录下添加子系统的构建配置文件ohos.build
完整内容如下:

{
  "subsystem": "mysubsys",
  "parts": {
    "hello": {
      "module_list": [
        "//mysubsys/hello/hellonapi:hellonapi"
      ],
      "inner_kits": [
      ],
      "system_kits": [
      ],
      "test_list": [
      ]
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 另外ohos.build里面不支持加注释,后面编译的时候会莫名其妙报错。别问,问就是笔者踩过坑了。(好像也没必要加注释)

需要明白以下知识点:

  "subsystem": "mysubsys",
  • 1
  • subsystem后面的mysubsy是子系统的名称。
  "parts": {
    "hello": {
   }
  }
  • 1
  • 2
  • 3
  • 4
  • hello是组件名称,被mysubsys子系统包含
"module_list": [
        "//mysubsys/hello/hellonapi:hellonapi"
  • 1
  • 2
  • hellonapi是模块名,被hello组件包含。

接着将子系统配置到源码下build\subsystem_config.json文件,在该文件中插入如下内容。

  "mysubsys": {
    "project": "hmf/mysubsys",
    "path": "mysubsys",
    "name": "mysubsys",
    "dir": ""
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • OpenHarmony系统架构中,子系统是一个逻辑概念,它具体由对应的组件构成。组件是对子系统的进一步拆分,可复用的软件单元,它包含源码、配置文件、资源文件和编译脚本;能独立构建,以二进制方式集成,具备独立验证能力的二进制单元。

本示例按子系统system > 组件part > 组件module 创建了3级目录

mysubsys                    -- 子系统目录
├── hello                   -- 组件目录
│   └── hellonapi           
│       ├── BUILD.gn        -- 组件module目录 
│       └── hellonapi.cpp   
└── ohos.build
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

源码实现

最后在组件目录下中创建代码文件hellonapi.cpp

完整内容如下:

#include <string.h>
#include "napi/native_node_api.h"
#include "napi/native_api.h"

// 接口业务实现C/C++代码
// std::string 需要引入string头文件,#include <string>
// 该napi_module对外具体的提供的API接口是 getHelloString
static napi_value getHelloString(napi_env env, napi_callback_info info) {
  napi_value result;
  std::string words = "Hello OpenHarmony NAPI";
  NAPI_CALL(env, napi_create_string_utf8(env, words.c_str(), words.length(), &result));
  return result;
}

// 注册对外接口的处理函数napi_addon_register_func
// 2.指定NAPI模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
// 模块对外接口注册函数为registerFunc
static napi_value registerFunc(napi_env env, napi_value exports)
{
    static napi_property_descriptor desc[] = {

        // 声明该napi_module对外具体的提供的API为getHelloString
        DECLARE_NAPI_FUNCTION("getHelloString", getHelloString),

    };
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
    return exports;
}

// 注册NAPI模块
// 1.先定义NAPI模块,指定当前NAPI模块对应的模块名
// 以及模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
// nm_modname: NAPI模块名称,对应eTS代码为import nm_modname from '@ohos.ohos_shared_library_name'
// 示例对应hap应用中eTS代码需要包含import hellonapi from '@ohos.hellonapi'
// 以下的出现的hellonapi都为注册的NAPI模块名
static napi_module hellonapiModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,

    // registerFunc是NAPI模块对外接口注册函数
    .nm_register_func = registerFunc, 

    .nm_modname = "hellonapi",  
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

// 3.NAPI模块定义好后,调用NAPI提供的模块注册函数napi_module_register(napi_module* mod)函数注册到系统中。
// register module,设备启动时自动调用此constructor函数,把定义的模块注册到OpenHarmony中。
// 以下出现的hellonapi都是注册的NAPI模块名
extern "C" __attribute__((constructor)) void hellonapiModuleRegister()
{
    // napi_module_register是ohos的NAPI组件提供的模块注册函数
    napi_module_register(&hellonapiModule);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

代码解析如下

接口业务实现C/C++代码
// 接口业务实现C/C++代码
// std::string 需要引入string头文件,#include <string>
// 该napi_module对外具体的提供的API接口是 getHelloString
static napi_value getHelloString(napi_env env, napi_callback_info info) {
  napi_value result;
  std::string words = "Hello OpenHarmony NAPI";
  NAPI_CALL(env, napi_create_string_utf8(env, words.c_str(), words.length(), &result));
  return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
添加NAPI接口头文件

NAPI提供了提供了一系列接口函数,声明包含如下2个头文件中,先添加这2个头文件到hellonapi.cpp

#include "napi/native_api.h"
#include "napi/native_node_api.h"
  • 1
  • 2
  • native_api.h和native_node_api.h这两个头文件
    • 在OpenHarmony3.1release源码中在//foundation/ace/napi/interfaces/kits目录下
    • 在OpenHarmony3.2beta3源码中分别在//foundation/arkui/napi/interfaces/kits和//foundation/arkui/napi/interfaces/inner_api目录下了。

注册NAPI模块、添加接口声明

定义的hellonapi模块,其对应结构体为napi_module。

  • 指定当前NAPI模块对应的模块名
  • 模块注册对外接口的处理函数,具体扩展的接口在该函数中声明。
// 注册对外接口的处理函数napi_addon_register_func
// 2.指定NAPI模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
// 模块对外接口注册函数为registerFunc
static napi_value registerFunc(napi_env env, napi_value exports)
{
    static napi_property_descriptor desc[] = {

        // 声明该napi_module对外具体的提供的API为getHelloString
        // 在napi_property_descriptor desc[]中需要将编写C语言的“getHelloString”方法与对暴露的Js语言的接口“getHelloString”进行关联。
        /*
         NAPI提供DECLARE_NAPI_FUNCTION(name, func)函数用于声明api,
         传入名称和其他实现函数。在registerFunc函数中添加DECLARE_NAPI_FUNCTION
       */
        DECLARE_NAPI_FUNCTION("getHelloString", getHelloString),

    };
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
    return exports;
}

// 注册NAPI模块
// 1.先定义NAPI模块,指定当前NAPI模块对应的模块名
// 以及模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
// nm_modname: NAPI模块名称,对应eTS代码为import nm_modname from '@ohos.ohos_shared_library_name'
// 示例对应hap应用中eTS代码需要包含import hellonapi from '@ohos.hellonapi'
// 以下的出现的hellonapi都为注册的NAPI模块名
static napi_module hellonapiModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,

    // registerFunc是该自定义的NAPI模块对外接口注册函数
    .nm_register_func = registerFunc, 

    .nm_modname = "hellonapi",  
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

// 3.NAPI模块定义好后,调用ohos的NAPI组件提供的模块注册函数napi_module_register(napi_module* mod)函数注册到系统中。
// register module,设备启动时自动调用constructor函数,把定义的模块注册到OpenHarmony中。
// 以下出现的hellonapi都是注册的NAPI模块名
extern "C" __attribute__((constructor)) void hellonapiModuleRegister()
{
    // napi_module_register(napi_module* mod)是ohos的NAPI组件提供的模块注册函数
    napi_module_register(&hellonapiModule);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • napi_module_register(napi_module* mod)是ohos的NAPI组件提供的模块注册函数。

    • 该函数在源码目录下foundation/arkui/napi/native_engine/native_node.cpp

注册NAPI模块总结

自定义子系统构建

hellonapi编译gn化,新增gn工程构建脚本。

在模块hellonapi目录下新建BUILD.gn文件,内容如下:

gn文件支持注释,以#开头

import("//build/ohos.gni")

#ohos_shared_library()中的hellonapi决定了生成动态库的名称,增量编译阶段生成动态库libhellonapi.z.so 

ohos_shared_library("hellonapi") {   

   include_dirs = [

   #NAPI头文件目录
   "//foundation/arkui/napi/interfaces/kits", 
   "//foundation/arkui/napi/interfaces/inner_api", 

   #根据增量编译阶段报错添加的头文件目录
   "//third_party/node/src"                       
  ]

   #根据增量编译时clang编译器报警,添加的cflag
  cflags_cc = [ 
   #编译时报错提示"-Werror",则加上"-Wno-error"
          "-Wno-error", 
   #编译时报错提示"-Wunused-function",则加上"-Wno-unused-function"
          "-Wno-unused-function", 
  ]

  #编译需要的源文件
  sources = [
    "hellonapi.cpp"
  ]

  #指定编译依赖libace_napi.z.so动态库
  deps = [ "//foundation/arkui/napi:ace_napi" ] 

  #指定库生成的路径
  #libhellonapi.z.so会安装在rk3568开发板的system/lib/module目录下
  relative_install_dir = "module"

  #子系统名称是mysubsys
  subsystem_name = "mysubsys"

  #组件名称是hello
  part_name = "hello"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

修改产品配置

将组件添加到需要的产品配置文件,源码目录下的productdefine/common/products/ohos-arm64.json。

  • 插入位置任意,但要注意行尾的逗号,确保格式json文件格式正确。
  "parts":{
    ...
    "mysubsys:hello":{},
    ...
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • mysubsys是本示例自定义的子系统名称
  • hello是自定义子系统下的组件名称
  • parts格式如下:
    "parts":{
        "部件所属子系统名:部件名":{}
    }
  • 1
  • 2
  • 3

修改build/subsystem_config.json

新增子系统定义。

  • subsystem_config.json文件定义了有哪些子系统以及这些子系统所在文件夹路径,添加子系统时需要说明子系统path与name,分别表示子系统路径和子系统名。

注意json文件也不支持注释!!!

  "mysubsys": {
    "project": "hmf/mysubsys",
    "path": "mysubsys",
    "name": "mysubsys"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • "path": "mysubsys",表示子系统路径
  • "name": "mysubsys"表示子系统名称

修改vendor/hihope/rk3568/config.json文件

将mysubsys子系统添加至rk3568开发板,在vendor目录下新增产品的定义。

    {
      "subsystem": "mysubsys",
      "components": [
        {
          "component": "hello",
          "features": []
        }
      ]
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • "subsystem": "mysubsys",表示添加的子系统是mysubsys
  • "component": "hello",表示添加的子系统中包含的组件名称是hello

编译烧录

先进行增量编译出子系统的动态库,增量编译没有报错后。再全量编译出镜像,将其烧录到开发板上

  • 增量编译命令
 ./build.sh --product-name rk3568 --ccache --build-target=hellonapi --target-cpu arm64
  • 1

  • 全量编译和烧录
    镜像文件在源码目录下位置如下:

调用接口

full-SDK替换(可选)

从OpenHarmony 3.2 Beta2起,SDK会同时提供Public SDK和Full SDK。通过DevEco Studio默认获取的SDK为Public SDK。
两者差异如下

  • Public SDK
    • 面向应用开发者提供,不包含需要使用系统权限的系统接口。通过DevEco Studio默认获取的SDK为Public SDK。
  • Full SDK
    • 面向OEM厂商提供,包含了需要使用系统权限的系统接口。使用Full SDK时需要手动从镜像站点获取,并在DevEco Studio中替换

笔者使用的DevEco Studio版本为3.0.0.993,即DevEco Studio 3.0。API为API9。

若提示找不到npm,需要配置一下环境变量,将以下路径添加到环境变量中即可

D:\DevEco Studio\ohos\sdk\ets\build-tools\ets-loader

创建OpenHarmony标准应用

新建项目,选择OpenHarmony。

compile sdk选择9,其他保持默认即可。

调用接口

第一步:调用方式和ArkUI框架提供的API一样,先import引入扩展的NAPI模块,后直接调用。

index.ets内容如下:

import prompt from '@system.prompt'
//显示文本弹窗

// 引入扩展的NAPI模块 
// 在hellonapi.cpp文件中定义nm_modname(模块名称)为hellonapi
// 在BUILD.gn文件中定义ohos_shared_library结构体名称为hellonapi
// 所以是import hellonapi from '@ohos.hellonapi'
import hellonapi from '@ohos.hellonapi'

@Entry
@Component
struct HelloNAPI {
  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Button("NAPI: hellonapi.getHelloString()").margin(10).fontSize(24).onClick(() => {

        // hellonapi.cpp对外具体的提供的API是getHelloString
        let strFromNAPI = hellonapi.getHelloString()

        prompt.showToast({ message: strFromNAPI })
      })
    }
    .width('100%')
    .height('100%')
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

第二步(可选):参考其他模块的.d.ts创建扩展模块@ohos.hellonapi.d.ts定义文件,放到IDE安装OpenHarmony SDK的目录路径ohos\sdk\ets\3.2.7.5\api下。

  • .d.ts文件的命名为@ohos.ohos_shared_library_name.d.ts,ohos_shared_library为BUID.gn文件中定义的动态库名称

@ohos.hellonapi.d.ts内容如下:

declare namespace hellonapi {
	function getHelloString(): string;
    /**
     * 
     *
     * @since 9
     * @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
     */

}
export default hellonapi;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • @since 9表示API的版本为9
  • @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore语句在.d.ts文件中一定要添加,否则IDE还是会报错找不到该文件。
  • declare namespace hellonapiexport default hellonapihellonapi是BUILD.gn中的定义的ohos_shared_library_name。
  • function getHelloString(): string;中的getHelloString()是hellonapi.cpp文件中指定的模块注册对外接口的处理函数

则IDE扫描如下:

标准应用编译不是强依赖OpenHarmony SDK,所以可忽略IDE中告警,直接编译打包hap。但是有的时候IDE会提示找不到@ohos.hellonapi.d.ts,然后有小概率的机会无法安装hap。这个时候就要参考ohos\sdk\ets\3.2.7.5\api下的.d.ts文件编写@ohos.hellonapi.d.ts了
如果不新建@ohos.hellonapi.d.ts放在sdk\ets\3.2.7.5\api,则IDE会报错

第三步:选择自动签名

第四步:将应用安装到dayu200开发板上

运行效果如下:

总结思考

  • 在本篇文章中添加的子系统目录下mysubsys/hello/hellonapi/BUILD.gn文件中有下图中这句代码,代表编译时mysubsys子系统生成的libhellonapi.z.so依赖编译出的foundation子系统下的arkui部件下的napi组件生成的libace_napi.z.so。

可以查阅ohos3.2beta3源码(笔者在撰写此篇文章时使用的是这个版本的源码)foundation/arkui/napi下的BUILD.gn文件验证

分析上图可以知道编译foundation子系统下的arkui部件下的napi组件生成libace_napi.z.so动态库。

知识点附送

Native API中支持的标准库

表1 OpenHarmony支持的标准库

名称简介
标准C库libc、libm、libdl组合实现C11标准C库。
标准C++库libc++ 是C++标准库的一种实现。
OpenSL ESOpenSL ES是一个嵌入式跨平台的音频处理库。
zlibZlib是基于C/C++语言实现的一个通用的数据压缩库。
EGLEGL是渲染API与底层原生窗口系统之间的一种标准的软件接口。
OpenGL ESOpenGL ES是一个嵌入式跨平台的为 3D 图形处理硬件指定标准的软件接口。

为了能让大家更好的学习鸿蒙(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://qr18.cn/F781PH

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/928385
推荐阅读
相关标签
  

闽ICP备14008679号