首页 > 其他分享 >【最新鸿蒙开发】带你简单了解Native适配开发

【最新鸿蒙开发】带你简单了解Native适配开发

时间:2024-09-08 21:23:05浏览次数:10  
标签:Node LOG 鸿蒙 适配 函数 nullptr API napi Native

引言

NDK(Native Development Kit)是HarmonyOS SDK提供的Native API、相应编译脚本和编译工具链的集合,方便开发者使用C或C++语言实现应用的关键功能。NDK只覆盖了HarmonyOS一些基础的底层能力,如C运行时基础库libc、图形库、窗口系统、多媒体、压缩库、面向ArkTS/JS与C跨语言的Node-API等,并没有提供ArkTS/JS API的完整能力。

  • NDK适用场景

    • 性能敏感的场景,如游戏,物理模拟等计算密集型场景

    • 需要复用已有C或C++库的场景

    • 需要针对CPU进行专项定制库的场景

今天,我们不聊别的,就专门来谈谈鸿蒙开发中的一大利器——Node-API。

Node-API简介

HarmonyOS Node-API是基于Node.js 8.x LTS的Node-API规范扩展开发的机制,为开发者提供了ArkTS/JS与C/C++模块之间的交互能力。它提供了一组稳定的、跨平台的API,可以在不同的操作系统上使用。

  • Node-API的关键交互流程

    image-20240831162526192

    ArkTs和C++之间的交互流程,主要分为以下两步:

    • 初始化阶段:当ArkTs侧在import一个Native模块时,ArkTS引擎会调用ModuleManager加载模块对应的so及其依赖。首次加载时会触发模块的注册,将模块定义的方法挂载到exports对象上并返回该对象

    • 调用阶段:当ArkTs侧通过上述import返回的对象调用方法时,ArkTs引擎会找到并调用对应的C/C++方法

Node-API的数据类型

一些常用的Node-API数据类型为:

数据类型描述
napi_status枚举数据类型,表示Node-API接口返回的状态信息,如操作成功与否的相关信息
napi_extended_error_info结构体,在调用函数不成功时存储了较为详细的错误信息
napi_value在C++代码中,表示一个JavaScript值
napi_env表示Node-API环境
napi_threadsafe_functionNode-API中的线程安全函数类型
napi_threadsafe_function_release_mode表示线程安全函数的释放模式
napi_threadsafe_function_call_mode表示线程安全函数的调用模式
napi_callback_infoNode-API回调信息类型
napi_callbackNode-API中的回调函数类型
napi_finalize表示资源清理函数类型
napi_async_execute_callback表示异步执行回调函数类型
napi_async_complete_callback表示异步完成回调函数类型
napi_cleanup_hook表示清理钩子函数类型
napi_async_cleanup_hook表示异步清理钩子函数类型
napi_throw表示抛出异常的函数
napi_throw_error表示抛出错误的函数
napi_throw_type_error表示抛出类型错误的函数
napi_throw_range_error表示抛出范围错误的函数
node_api_throw_syntax_error表示抛出语法错误的函数
napi_is_error表示检查错误的函数
napi_create_error表示创建错误的函数
napi_create_type_error表示创建类型错误的函数
napi_create_range_error表示创建范围错误的函数
node_api_create_syntax_error表示创建语法错误的函数
napi_get_and_clear_last_exception表示获取并清除最后异常的函数
napi_is_exception_pending表示检查是否有挂起的异常的函数
napi_fatal_exception表示处理致命异常的函数
napi_open_handle_scope表示打开句柄作用域的函数
napi_close_handle_scope表示关闭句柄作用域的函数
napi_open_escapable_handle_scope表示打开可逃逸句柄作用域的函数
napi_close_escapable_handle_scope表示关闭可逃逸句柄作用域的函数
napi_escape_handle表示逃逸句柄的函数
napi_create_reference表示创建引用的函数
napi_delete_reference表示删除引用的函数
napi_reference_ref表示引用引用计数的函数
napi_reference_unref表示解除引用引用计数的函数
napi_get_reference_value表示获取引用值的函数
napi_add_env_cleanup_hook表示添加环境清理钩子的函数

使用Node-API实现跨语言交互开发流程

使用Node-API实现跨语言交互,首先需要按照Node-API的机制实现模块的注册和加载等相关动作。

  • ArkTS/JS侧:实现C++方法的调用。代码比较简单,import一个对应的so库后,即可调用C++方法。

  • Native侧:.cpp文件,实现模块的注册。需要提供注册lib库的名称,并在注册回调方法中定义接口的映射关系,即Native方法及对应的JS/ArkTS接口名称等。

  • 下面实现一个例子(参考官方)

  • image-20240902091523045

目录结构

Native侧

基于Node—API开发业务功能

//types/hello.cpp
//基于Node-API开发业务能力
static napi_value MyHypot(napi_env env, napi_callback_info info)
{
    if( (nullptr == env) || (nullptr == info)){
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot",  "env or exports is nullptr");
        return nullptr;
    }
    
    //参数个数
    size_t argc = 2;
    
    //声明参数数组
    napi_value args[2] = {nullptr};
    
    //获取参数
    if( napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)){
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot",  "api_get_cb_info failed");
        return nullptr;
    }
    
    //转化参数数组为double类型
    double valueX = 0.0;
    double valueY = 0.0;
    if( napi_ok != napi_get_value_double(env, args[0], &valueX) || napi_ok != napi_get_value_double(env, args[1], &valueY)){
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot",  "api_get_value_double failed");
        return nullptr;
    }
    
    //使用hypot方法计算结果
    double result = hypot(valueX, valueY);
    
    napi_value napiResult;
    if( napi_ok != napi_create_double(env, result, &napiResult)){
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "MyHypot",  "api_create_double failed");
        return nullptr;
    }
    return napiResult;
}

 接口映射

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    if( (nullptr == env) || (nullptr == exports)){
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init",  "env or exports is nullptr");
        return exports;
    }
    // 要添加的方法,这里根据方法修改数组
    napi_property_descriptor desc[] = {
        { "myHypot", nullptr, MyHypot, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    if( napi_ok != napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)){
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init",  "api_define_properties failed");
        return nullptr;
    }
    return exports;
}
EXTERN_C_END

 模块注册


static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    //名字要为接口映射定义的函数名
    .nm_register_func = Init,
    //定义模块名称,ArkTs侧引入so库的名称
    .nm_modname = "hello",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};


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

 模块配置构建(CMakeLists)

# the minimum version of CMake.
//types/CMakeLists
cmake_minimum_required(VERSION 3.5.0)
project(mycppdemo)

set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

if(DEFINED PACKAGE_FIND_FILE)
    include(${PACKAGE_FIND_FILE})
endif()

include_directories(${NATIVERENDER_ROOT_PATH}
                    ${NATIVERENDER_ROOT_PATH}/include)

find_library(
    #查找hilog_ndk.z库的位置,存储到hilog-lib的中,适用于不清楚依赖库的具体路径
    hilog-lib
    hilog_ndk.z
)
//注意下面的两个hello
//由hello.cpp生成动态库,这里前面叫做hello,编译出来后就是libhello.so
add_library(hello SHARED hello.cpp)
//设置目标需要链接的库
target_link_libraries(hello PUBLIC ${hilog-lib} libace_napi.z.so libc++.a)
//build-profile.json5
{
  "apiType": 'stageMode',
  "buildOption": {
    "externalNativeOptions": {
      "path": "./src/main/cpp/CMakeLists.txt",
      "arguments": "",
      "abiFilters": [
        "arm64-v8a",
        "x86_64"
      ],
      "cppFlags": "",
    }
  },
  "targets": [
    {
      "name": "default",
      "runtimeOS": "HarmonyOS"
    }
  ]
}

导出Native接口1

//types/libhello/index.d.ts
//libhello与编译后的名称对应
export const myHypot: (a: number, b: number) => number;

导出Native接口2

//oh-package.json5
{
  "license": "",
  "devDependencies": {
    "@types/libhello.so": "file:./src/main/cpp/types/libhello"
  },
  "author": "",
  "name": "entry",
  "description": "Please describe the basic information.",
  "main": "",
  "version": "1.0.0",
  "dependencies": {}
}

ArkTs侧

ets文件中使用Native-API

import libHello from 'libhello.so';
import CommonContants from '../../common/CommonContants';

@Entry
@Component
struct NativeTemplate {
  @State result: string = '0';
  @State numX: number = 0.0;
  @State numY: number = 0.0;
  private textInputControllerX: TextInputController = new TextInputController();
  private textInputControllerY: TextInputController = new TextInputController();

  build() {
    Column() {
      //...
      Row() {
        Button($r('app.string.submit_button'))
          .fontSize($r('app.float.submit_button_font_size'))
          .fontWeight(CommonContants.FONT_WEIGHT)
          .height(CommonContants.FULL_PARENT)
          .width($r('app.float.button_width'))
          .onClick(() => {
            //使用c++函数
            let resultTemp = libHello.myHypot(this.numX, this.numY);
            if (resultTemp > CommonContants.MAX_RESULT) {
              this.result = resultTemp.toExponential(CommonContants.EXPONENTIAL_COUNT);
            } else {
              this.result = resultTemp.toFixed(CommonContants.FIXED_COUNT);
            }
          })
      }
      .height($r('app.float.button_height'))
      .width(CommonContants.FULL_PARENT)
      .justifyContent(FlexAlign.Center)
      .margin({ top: $r('app.float.button_margin_top') })
    }
    .width(CommonContants.FULL_PARENT)
    .height(CommonContants.FULL_PARENT)
    .backgroundColor($r('app.color.background_color'))
  }
}

CommonContants.ets文件
 

export default class CommonConstants {

  /**
   * Height and width is 100%.
   * */
  static readonly FULL_PARENT: string = '100%';

  /**
   * Font weight.
   * */
  static readonly FONT_WEIGHT: number = 500;

  /**
   * Max lines.
   */
  static readonly MAX_LINES: number = 1;

  /**
   * TextInput layout weight.
   */
  static readonly TEXTINPUT_LAYOUT_WEIGHT: number = 1;

  /**
   * Bigger than 999999999 using scientific notation.
   */
  static readonly MAX_RESULT: number = 999999999;

  /**
   * Exponential count.
   */
  static readonly EXPONENTIAL_COUNT = 9;

  /**
   * Fixed count.
   */
  static readonly FIXED_COUNT = 2;
}

结语

通过上面的示例,我们可以看到,使用Node-API实现跨语言交互开发流程并不复杂。只需要掌握好相关的接口和步骤,你就能够轻松地在C/C++和ets文件之间进行数据传输和函数调用。当然,这只是一个简单的示例,实际开发中可能会遇到更加复杂的情况和问题。但无论如何,我相信只要你肯下功夫去学习和实践,你一定能够成为鸿蒙开发的佼佼者!

标签:Node,LOG,鸿蒙,适配,函数,nullptr,API,napi,Native
From: https://blog.csdn.net/m0_66825548/article/details/142033048

相关文章

  • C++STL之stack和queue容器适配器:基本使用及模拟实现
    目录stack的介绍和使用stack的介绍stack的使用queue的介绍和使用queue的介绍queue的使用priority_queue的介绍和使用priority_queue的介绍priority_queue的使用deque双端队列(容器)deque的介绍及使用deque的缺点deque的原理(了解)容器适配器概念stack和queue的......
  • 【鸿蒙实战开发】基于加解密算法框架的常见规格问题
    往期知识点整理鸿蒙(HarmonyOS)北向开发知识点记录~【鸿蒙实战开发】ArkTS多线程的多线程系列(一):ArkTS多线能力入门【鸿蒙实战开发】ArkTS多线程的多线程系列(二):基于Sendable共享对象实现跨线程通信及UI状态刷新【鸿蒙实战开发】ArkTS多线性的多线程系列(三):基于单例实现跨......
  • 华为鸿蒙手机安装好谷歌商店,GMS服务,谷歌三件套,谷歌框架后,下载的一些应用无法正常定位,
    最近有有小伙伴问为什么他下载好一些定位的软件,能正常的进入,定位权限也是开启的,但是就是定不了位,,给我们小伙伴带来了烦恼,那我们怎么解决呢?我这里给大家讲解一下,避免大家以后遇见后不知道怎么去解决解决方法1.首先我们需要在我们的手机设置里面找到我们的应用和服务,进去后点......
  • 鸿蒙HarmonyOS入门篇第一天 组件-样式-基础
    1.常用的系统组件Text显示文本lmage显示图片Colum列,内容垂直排列row行,内容水平排列button按钮 2.通用属性wight宽height高backgroundColor背景色3.尺寸单位1.px物理像素,也叫设备像素,设备实际拥有的像素点(出场设置、分辨率单位)问题:如果用px作为宽高单......
  • iOS端-多语言国际化适配方案,组件化多语言适配
    一.普通文案适配1.组件内创建Strings文件并添加适配文案(1).创建Strings文件,如下图命名一般以系统名Localizable命名即可,如下图所示。如果起其他名称,需要在后面步骤中添加对应tbl名称,详细见后面步骤二中的第3点(2).将需要适配的语言加入到strings文件中,勾选Localization下Lo......
  • Design Patterns for Cloud Native Applications
    研究背景研究问题:本书旨在解决如何构建云原生应用的问题。云原生应用具有成本节约和可扩展性的优势,但其开发和部署面临诸多挑战。研究难点:云原生应用的复杂性在于如何在动态环境中设计和开发可扩展、可靠、可管理和可观测的应用。相关工作:本书参考了现有的架构原则和实......
  • 鸿蒙-TypeScript语法
    1.概述HarmonyOS应用的主要开发语言是ArkTS,它由TypeScript(简称TS)扩展而来,在继承TypeScript语法的基础上进行了一系列优化,使开发者能够以更简洁、更自然的方式开发应用。注意:TypeScript本身也是由另一门语言JavaScript扩展而来,它主要是在JavaScript的基础上添加了静......
  • BRAM IP Native模式使用
    简介    BRAM(‌BlockRAM)是‌FPGA(Field-ProgrammableGateArray)中的一种专用RAM资源,固定分布在FPGA内部的特定位置。该内容主要对BRAM(BlockRAM”的缩写)Native模式下IP界面做详细描述和使用。界面介绍Basic界面          1、选择对外接口,可以是AXI总线......
  • 鸿蒙原生应用元服务开发-仓颉基础数据类型Unit与Nothing类型
    一、Unit类型对于那些只关心副作用而不关心值的表达式,它们的类型是Unit。例如,print函数、赋值表达式、复合赋值表达式、自增和自减表达式、循环表达式,它们的类型都是Unit。Unit类型只有一个值,也是它的字面量:()。除了赋值、判等和判不等外,Unit类型不支持其他操作。二、Nothing......
  • 免费开源的低代码表单FormCreate安装教程,支持可视化设计,适配移动端
    低代码表单FormCreate是一个可以通过JSON生成具有动态渲染、数据收集、验证和提交功能的表单生成组件。它支持6个UI框架,适配移动端,并且支持生成任何Vue组件。内置20种常用表单组件和自定义组件,再复杂的表单都可以轻松搞定源码地址:Github|Gitee特点多平台......