OpenHarmony开发者论坛

标题: 浅谈OpenHarmony4.0的NDK开发 [打印本页]

作者: 深开鸿_王石    时间: 2024-1-19 09:10
标题: 浅谈OpenHarmony4.0的NDK开发
[md]### 背景

#### Native API(NDK)入门

**Native API是OpenHarmony SDK上提供的一组native开发接口与工具集合(也称为NDK),方便开发者使用C或者C++语言实现应用的关键功能。Native API只覆盖了OHOS基础的一些底层能力,如libc,图形库,窗口系统,多媒体,压缩库等,并没有完全提供类似于JS API上的完整的OHOS 平台能力。在应用中使用Native API会编译成动态库打包到应用中。**

#### 名词概念

| **名词**       | **名词解释**                                                                                                                                                                          |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Native API** | **OHOS SDK里面native包提供的,面向三方应用开发的Native 接口以及相应编译脚本,编译工具链。包括C运行时基础库libc,3D图形库opengl,面向JS与C跨语言的接口Node-API等,具体内容详见下表。** |
| **NDK**        | **Native Develop Kit的缩写,在OHOS上就是Native API;Native API是官方名字,NDK指代相同意思。**                                                                                         |
| **SDK CAPI**   | **OHOS Native API中的C语言接口,以及工具链部分,当前OHOS的Native API里面只包含C语言接口,因此Native API与CAPI意思一样,建议交流的时候使用CAPI,防止Native API与napi缩写混用。**       |
| **Node-API**   | **曾用名napi,是OHOS中提供JS与C跨语言调用的接口,是Native API接口中的一部分. 该接口在Node.js提供的Node-API基础上扩展而来,但不完全与Node.js中的Node-API完全兼容。**                   |
| **napi**       | **Node-API的曾用名,当前Node-API头文件中的接口仍然以napi\_开头,不建议使用。**                                                                                                        |

## Native API构成介绍

### Native API目录结构

**Native API在SDK包的位置为\$(SDK\_ROOT)/native目录,主要有以下几个部分组成**

| **目录**        | **功能说明**                                                                                          |
| --------------------- | ----------------------------------------------------------------------------------------------------------- |
| **build**       | **应用中编译动态库的toolchain cmake脚本;这个目录下ohos.toolchain.cmake文件定义了给OHOS交叉编译选项** |
| **build-tools** | **放置编译构建的工具,如cmake**                                                                       |
| **docs**        | **Native API接口参考文档,通过doxgen从头文件中提取出来**                                              |
| **llvm**        | **支持OHOS ABI的llvm交叉编译器**                                                                      |
| **sysroot**     | **放置编译链接的依赖文件目录,包含头文件,动态库等**                                                  |

### Native API接口(4.0 Release)

| **接口分类**                           | **接口功能**                                                                                      | **引入版本** |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------ |
| [标准C库](native-lib/third_party_libc/musl.md)  | **以musl为基础提供的标准c库接口,当前提供了1500+的接口**                                          | **8**        |
| [标准C++库](native-lib/third_party_libc/cpp.md) | **c++运行时库libc++\_shared,此库在打包的时候需要打包或者静态链接到应用中**                       | **8**        |
| **日志**                               | **打印日志到系统的hilog接口**                                                                     | **8**        |
| **Node-API**                           | **ArkUI提供的,方便应用开发接入JS应用环境的一组类Node-API(也叫napi),是属于Native API的一部分** | **8**        |
| **XComponent**                         | **ArkUI XComponent组件中的surface与触屏事件接口,方便开发者开发高性能图形应用**                   | **8**        |
| **libuv**                              | **ArkUI集成的三方的异步IO库**                                                                     | **8**        |
| **libz**                               | **zlib库,提供基本的压缩,解压接口**                                                              | **8**        |
| **Drawing**                            | **系统提供的2D图形库,可以在surface进行绘制**                                                     | **8**        |
| **OpenGL**                             | **系统提供的openglv3接口**                                                                        | **8**        |
| **Rawfile**                            | **应用资源访问接口,可以读取应用中打包的各种资源**                                                | **8**        |
| **OpenSLES**                           | **用于2D,3D音频加速的接口库**                                                                    | **8**        |
| **Mindspore**                          | **AI模型接口库**                                                                                  | **9**        |
| **包管理**                             | **包服务接口,方便查询应用包信息**                                                                | **8**        |

### Native API相关资料

* [Native API参考](native-apis/_o_h___native_x_component.md),介绍各个API参考手册
* [Native API中支持的标准库](../reference/native-lib/third_party_libc/musl.md),介绍Native API支持的开源标准库
* [Native API开发指南](../napi/napi-guidelines.md),结合具体的例子,场景介绍各类接口的使用
* [如何在Cmake工程中使用NDK](../faqs/cmake-with-ndk.md),介绍如何使用使用NDK开发一个CMake工程
* [Node-API在应用工程中的使用指导](../napi/napi-guidelines.md), 如何使用Node-API接口

### 简单应用

##### 如何开发应用?

* **DevEco IDE创建工程选择“Native C++”模板:**

  ![createproject.png](https://forums-obs.openharmony.c ... qfzr8v5n7r8ojyq.png "createproject.png")

  ![应用.png](https://forums-obs.openharmony.c ... npezr2ez4rzpmyw.png "应用.png")

  **编译运行后,点击helloworld打印输出有:Test NAPI 2 + 3 = 5**
* **ArkUI部分:**

  ```
  import hilog from '@ohos.hilog';           //导入hilog
  import testNapi from 'libentry.so';        //导入nativeC++模块

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

    build() {
      Row() {
        Column() {
          Text(this.message)
            .fontSize(50)
            .fontWeight(FontWeight.Bold)
            .onClick(() => {
              //调用nativeC++代码
              hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3));
            })
        }
        .width('100%')
      }
      .height('100%')
    }
  }
  ```
* **nativeC++部分由 CMake 和 C++代码两部分组成:**

  * **CMake:**
    ```
    # the minimum version of CMake.
    cmake_minimum_required(VERSION 3.4.1)
    project(MyNDKApplication)
    # 编译路径
    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
    # 编译头文件路径
    include_directories(${NATIVERENDER_ROOT_PATH}
                        ${NATIVERENDER_ROOT_PATH}/include)
    # 编译对象entry是对应用层可见的so,即import testNapi from 'libentry.so'
    add_library(entry SHARED hello.cpp)
    # 这是link命令,libace_napi 这个就是node-api需要用的so库;
    target_link_libraries(entry PUBLIC libace_napi.z.so)
    ```
  * **C++:**
    ```
    #include "napi/native_api.h"
    // 对外node-api方法,对应testNapi.add(2, 3)
    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
    // 模块声明,import时候调用
    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);
    }
    ```

##### 如何使用系统NDK?

**上面例子运行起来后,c++部分是没有打印信息的,若想看到对应的打印信息,则需要调用hilog进行输出,修改如下:**

* **cmake修改:**
  ```
  # the minimum version of CMake.
  cmake_minimum_required(VERSION 3.4.1)
  project(MyNDKApplication)

  set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

  include_directories(${NATIVERENDER_ROOT_PATH}
                      ${NATIVERENDER_ROOT_PATH}/include)
  # 增加hiloglib库引用
  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(entry SHARED hello.cpp)
  # 增加hiloglib库连接
  target_link_libraries(entry PUBLIC ${hilog-lib} libace_napi.z.so)
  ```
* **c++文件修改:**
  ```
  // 增加hilog头文件
  #include <hilog/log.h>
  #include "napi/native_api.h"

  static napi_value Add(napi_env env, napi_callback_info info)
  {
      // 增加打印输出
      const unsigned int LOG_PRINT_DOMAIN = 0xFF00;
      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "Init begins");
      size_t requireArgc = 2;
      size_t argc = 2;
      napi_value args[2] = {nullptr};
      
  ...
  ```
* **输出:**
  ```
  08-07 05:40:25.079  15167-15167  A0ff00/Init                    com.example.myndkapplication    I  Init begins
  08-07 05:40:25.079  15167-15167  A00000/testTag                 com.example.myndkapplication    I  Test NAPI 2 + 3 = 5
  ```

##### 具体原理是什么?

**1、PC端OHOS SDK里包括了native对应的库文件和头文件;**

![sdk](file://D:\gitee\workdoc\sharedoc\%E8%B7%9F%E7%9D%80%E5%B0%8F%E7%99%BD%E4%B8%80%E8%B5%B7%E5%AD%A6%E9%B8%BF%E8%92%99%5B%E5%9B%9B%E5%8D%81%E5%9B%9B%5D%20%E6%B5%85%E8%B0%884.0%E7%9A%84NDK%E5%BC%80%E5%8F%91\sdk.png?lastModify=1705626489)

**2、OHOS源码可以编译出带NDK的FullSDK,也可以从CI网址下载;**

```
# Generate NDK library from NDK description file.
#
# Variables:
#  ndk_description_file:
#  min_compact_version: string specifies the minimal compactible version of NDK.
#    set to major_version in default.
#
template("ohos_ndk_library") {
 forward_variables_from(invoker, [ "testonly" ])
 assert(defined(invoker.ndk_description_file),
        "ndk description file is necessary ")
...
// 在GN里用ohos_ndk_library生成ndk库和头文件,如
   
ohos_ndk_library("libhilog_ndk") {
 output_name = "hilog_ndk"
 ndk_description_file = "./libhilog.ndk.json"
 min_compact_version = "1"
 system_capability = "SystemCapability.HiviewDFX.HiLog"
}

ohos_ndk_headers("hilog_header") {
 dest_dir = "$ndk_headers_out_dir/hilog"
 sources = [ "./include/hilog/log.h" ]
}

// ndk_description_file 对应的 libhilog.ndk.json 文件里声明了导出的接口函数
[
   {
       "name": "OH_LOG_Print"
   },
   {
       "name": "OH_LOG_IsLoggable"
   }
]

// ohos 编译fullsdk的命令: ./build.sh --product-name ohos-sdk
```

**ohos的ci网址如下:**[每日构建 | OpenHarmony CI](http://ci.openharmony.cn/workbench/cicd/dailybuild/dailylist)

![ci](file://D:\gitee\workdoc\sharedoc\%E8%B7%9F%E7%9D%80%E5%B0%8F%E7%99%BD%E4%B8%80%E8%B5%B7%E5%AD%A6%E9%B8%BF%E8%92%99%5B%E5%9B%9B%E5%8D%81%E5%9B%9B%5D%20%E6%B5%85%E8%B0%884.0%E7%9A%84NDK%E5%BC%80%E5%8F%91\ci.png?lastModify=1705626489)

### 使用建议

##### 建议使用Native API的场景

**主要有如下一些**

1. **应用性能敏感代码,比如游戏,物理模拟等计算密集型场景**
2. **需要复用已有的C或C++库**
3. **需要针对CPU特性进行专项定制的库,如neon加速**

##### 不建议使用Native API的场景

1. **写一个纯native的的OHOS应用**
2. **希望在尽可能多的OHOS设备上保持兼容的应用**

##### 维测能力

1. **OHOS官方提供lldb remote方式代码调试,详细参看**[lldb参考手册](https://gitee.com/openharmony/th ... r/lldb/README_zh.md)。
2. **musl库的log维测能力,请参看**[libc库](./native-lib/third_party_libc/musl.md)维测章节。

### 总结

* **NDK方式是应用层直接调用底层库或者三方库目前看最常规的方式;**
* **4.0(API10)有ndk 46个,3.2(API9)有ndk 28个,实质代码里有更多的ndk,RK的原因没有编出更多,比如sensor部分就没有编译出来;**
* **划重点,NDK里有两种api:node-api,用于arkts和c++调用和传递数据;c/c++ api,可以把动态库打包成应用侧c++直接调用的接口;**
* **社区文档:**[https://gitee.com/openharmony/do ... ation-dev/reference](https://gitee.com/openharmony/do ... ation-dev/reference)。

**PS:附件是这个用例里的代码和3.2,4.0里扫出的ndk,有需要自取。**
[/md]
作者: yjka    时间: 2024-4-3 16:25
CMakeList.txt 中使用下面的配置还是会报错
# 增加hiloglib库引用
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
)
改成
target_link_libraries(libName PUBLIC libace_napi.z.so libhilog_ndk.z.so)
不会报错
作者: 深开鸿_王石    时间: 2024-4-7 17:11
回复 yjka: 这么神奇吗?我ide版本是Build Version: 4.0.0.600, built on October 17, 2023,是不是这个有区别,我这边这样用都是okay的,sample仓也是这么用的
作者: yjka    时间: 2024-4-7 19:49
回复 深开鸿_王石: 我这边环境是
DevEco Studio 3.1.1 Release
Build Version: 3.1.0.501, built on June 20, 2023
API10
需要修改后才行


作者: 深开鸿_王石    时间: 2024-4-8 18:26
回复 yjka: 那就说通了,哈哈,ide版本不同了,谢谢提供信息
作者: armstrongceng    时间: 2024-7-24 11:20
请教一下,NDK中,如何使用第三方so库呢?官方文档写的太简洁了。




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5