鸿蒙OS开发实例:【Native C++】

介绍

本篇Codelab主要介绍如何使用DevEco Studio创建一个Native C++应用。应用采用Native C++模板,实现使用NAPI调用C标准库的功能。使用C标准库hypot接口计算两个给定数平方和的平方根。在输入框中输入两个数字,点击计算结果按钮显示计算后的数值。

相关概念

  • [Native API]:NAPI提供的接口名与三方Node.js一致,目前支持部分接口。
  • [Native API中支持的标准库]:目前支持标准C库、C++库、OpenSL ES、zlib。

环境搭建

软件要求

  • [DevEco Studio]版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。
  • 鸿蒙文档参考:gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或复制前往学习。

硬件要求

  • 开发板类型:[润和RK3568开发板]。
  • OpenHarmony系统:3.2 Release。
  • 鸿蒙NEXT学习文档紫料可mau123789添加v直接领

搜狗高速浏览器截图20240326151450.png

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. [获取OpenHarmony系统版本]:标准系统解决方案(二进制)。以3.2 Release版本为例:

  2. 搭建烧录环境。

    1. [完成DevEco Device Tool的安装]
    2. [完成RK3568开发板的烧录]
  3. 搭建开发环境。

    1. 开始前请参考[工具准备],完成DevEco Studio的安装和开发环境配置。
    2. 开发环境配置完成后,请参考[使用工程向导]创建工程(模板选择“Empty Ability”)。
    3. 工程创建完成后,选择使用[真机进行调测]。

代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在gitee中提供。

使用Native C++模板创建项目会自动生成cpp文件夹、types文件夹、CMakeList.txt文件,开发者可以根据实际情况自行添加修改其他文件及文件夹。

├──entry/src/main
│  ├──common
│  │  └──CommonContants.ets               // 常量定义文件
│  ├──cpp                                 // C++代码区
│  │  ├──CMakeLists.txt                   // CMake编译配置文件
│  │  ├──hello.cpp                        // C++源代码
│  │  └──types                            // 接口存放文件夹
│  │     └──libhello
│  │        ├──index.d.ts                 // 接口文件
│  │        └──oh-package.json5           // 接口注册配置文件
│  └──ets                                 // 代码区
│     ├──entryability
│     │  └──EntryAbility.ts               // 程序入口类
│     └──pages
│        └──Index.ets                     // 主界面
└──entry/src/main/resources               // 资源文件目录

架构组成

应用架构

应用架构可以分为三部分:C++、ArkTS、工具链。

  • C++:包含各种文件的引用、C++或者C代码、Native项目必需的配置文件等。
  • ArkTS:包含界面UI、自身方法、调用引用包的方法等。
  • 工具链:包含CMake编译工具在内的系列工具。

使用ArkTS调用C++方法的过程中,需要使用到NAPI、CMake等工具来做中间转换,整个架构及其关联关系参考示意图。

示意图中,hello.cpp文件实现C++方法,并通过NAPI将C++方法与ArkTS方法关联。

C++代码通过CMake编译工具编译成动态链接库so文件,使用index.d.ts文件对外提供接口。ArkTS引入so文件后调用其中的接口。

编译架构

ArkTS与C++方法的调用、编译流程参考示意图。图中C++代码通过CMake编译生成so文件后可以直接被ArkTS侧引入,最终通过hvigor编译成可执行的hap包。

Native项目开发流程

Native侧操作详解

  1. 配置模块描述信息,设置Init方法为napi_module的入口方法。attribute((constructor))修饰的方法由系统自动调用,使用NAPI接口napi_module_register()传入模块描述信息进行模块注册。Native C++模板创建项目会自动生成此结代码,开发者可根据实际情况修改其中内容。

    // hello.cpp
    static napi_module demoModule = {nm_version = 1,nm_flags = 0,nm_filename = nullptr,nm_register_func = Init,         // napi_module入口方法nm_modname = "hello",            // napi_module模块名nm_priv = ((void *)0),reserved = { 0 }
    };extern "C" __attribute__((constructor)) void RegisterModule(void) {napi_module_register(&demoModule);
    }
    
  2. Init方法为Native C++模板生成的结构,开发者可根据实际情况修改其中内容。在napi_property_descriptor desc[]中,我们需要将编写的MyHypot方法与对外提供的接口myHypot接口进行关联,其他参数使用示例默认值填写。使用NAPI接口napi_define_properties构建包含方法对应列表的返回值。

    // hello.cpp
    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 null");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", "napi_define_properties failed");return nullptr;}return exports;
    }
    
  3. 本例中使用C标准库的hypot方法进行计算。引入C标准库头文件math.h,使用double类型解析传入的参数后,调用C标准库方法hypot计算两数平方的和后计算平方根。使用NAPI接口napi_create_double将结果转化为napi_value类型的变量并返回。

    // hello.cpp
    #include <hilog/log.h>
    #include "napi/native_api.h"
    #include "math.h"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 null");return nullptr;}// 参数数量size_t argc = PARAMETER_COUNT;// 定义参数数组napi_value args[PARAMETER_COUNT] = { 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", "napi_get_value_double failed");return nullptr;}// 调用C标准库的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", "napi_create_double failed");return nullptr;}return napiResult;
    }
    
  4. 添加接口文件以及接口配置文件。接口文件index.d.ts用于对外提供方法说明。接口配置文件oh-package.json5文件中将index.d.ts与CMake编译的so文件关联起来。模块级目录下oh-package.json5文件添加so文件依赖。

    // index.d.ts
    export const myHypot: (a: number, b: number) => number;
    
    // oh-package.json5
    {"name": "libhello.so","types": "./index.d.ts"
    }// entry/oh-package.json5
    {"devDependencies": {"@types/libhello.so": "file:./src/main/cpp/types/libhello"}
    }
    
  5. 在CMakeLists.txt文件中配置CMake编译参数。配置需要添加的hello.cpp文件,编译后的so文件名为libhello.so。CMakeLists.txt是CMake编译的配置文件,里面的大部分内容无需修改,project、add_library方法中的内容可以根据实际情况修改。

    # CMakeLists.txt
    # 声明使用 CMAKE 的最小版本号
    cmake_minimum_required(VERSION 3.4.1)# 配置项目信息
    project(NativeTemplateDemo)# set命令,格式为set(key value),表示设置key的值为value
    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})# 设置头文件的搜索目录
    include_directories(${NATIVERENDER_ROOT_PATH}${NATIVERENDER_ROOT_PATH}/include
    )# 添加日志库
    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
    )# 添加名为hello的库,库文件名为libhello.so
    add_library(hello SHARED hello.cpp)# 添加构建需要链接的库
    target_link_libraries(hello PUBLIC ${hilog-lib} libace_napi.z.so libc++.a)
    

    说明:

    • CMAKE_CURRENT_SOURCE_DIR:CMakeList.txt文件所在的目录。
    • add_library:添加本地的cpp文件,多cpp文件使用空格或换行间隔。
    • target_link_libraries:添加需要链接的库,本篇Codelab使用C标准库hypot方法,此处链接libc++.a库文件。

ArkTS调用C++方法

Index.ets文件使用import语句导入CMake编译出的so文件。Button组件添加点击事件,点击按钮触发点击事件时,调用libhello.so对外提供的myHypot方法,执行计算并返回计算结果。依据结果值进行格式化,显示科学计数法或保留指定位小数。

// Index.ets
import libHello from 'libhello.so';@Entry
@Component
struct Index {...build() {...Button($r('app.string.submit_button')).onClick(() => {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);}})}
}

界面设计

界面由标题、文本说明、计算结果展示、输入框、按钮组成。Index.ets文件完成界面实现,使用Column及Row容器组件进行布局。

// Index.ets
@Entry
@Component
struct NativeTemplate {...build() {Column() {...Column() {...Row() {...TextInput({ controller: this.textInputControllerX }).type(InputType.Number)}.height($r('app.float.tips_num_height')).width(CommonContants.FULL_PARENT)Row() {...TextInput({ controller: this.textInputControllerY }).type(InputType.Number).onChange(value => {this.numY = parseFloat(value);})}.height($r('app.float.tips_num_height')).width(CommonContants.FULL_PARENT)}Row() {Button($r('app.string.submit_button')).height(CommonContants.FULL_PARENT).width($r('app.float.button_width'))}.height($r('app.float.button_height')).width(CommonContants.FULL_PARENT)}.width(CommonContants.FULL_PARENT).height(CommonContants.FULL_PARENT)}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/811319.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

lanqiao.602 迷宫

题目&#xff1a; 代码&#xff1a; #include<iostream> #include<cstring> #include<algorithm> #include<queue> using namespace std; char mp[31][51]; //稍微开大一点 char k[4]{D,L,R,U}; //按字典序记录路径 int dirx[]{1,0,0,-1},d…

学习ArkTS -- 常用组件使用

学习ArkTS 使用Deveco studio写ArkTSImage: 图片显示组件1.声明Image组件并设置图片源2. 添加图片属性 Text: 文本显示组件1. 声明Text组件并设置文本内容2. 添加文本属性 TextInput&#xff1a;文本输入框1. 声明TextInput2. 添加属性和事件 Button 组件1. 声明Button组件&…

关于“使用java中的二维矩阵方法生成二维码“ 以及 “Java加载外部字体文件时出错的原因“

生成二维码 铁铁们,这两日写了一个导出二维码的接口,要求有一个是在二维码下方生成字体,现在奉上生成二维码的代码: controller层 Operation(summary "导出机构二维码",description "导出机构二维码")GetMapping("/orgCode")public void getO…

Java-Doc

Java-Doc javdoc命令是用来生成自己的API文档的 参数信息&#xff1a;author作者名version版本号since知名需要最早使用的jdk版本param参数名return返回值情况throws异常抛出情况 1.参数信息的使用&#xff1a; 未完待续... ...

HashMap部分底层源码解析

哈希表的物理结构 HashMap底层都是哈希表&#xff08;也称散列表&#xff09;&#xff0c;线程不安全&#xff0c;其中维护了一个长度为2的幂次方的Entry类型的数组table&#xff0c;数组的每一个索引位置被称为一个桶(bucket)&#xff0c;你添加的映射关系(key,value)最终都被…

腾讯云向量数据库-RAG介绍

1.说明 RAG结合LLM(通用大预言模型)构件基于私有文档、专业领域知识、实时信息的charbot。 2.RAG的主要步骤 知识切片成chunk向量化chunk入库query检索知识chunk构件prompts调用llm生成回答 3.优势 快速构件demo快速理解rag社区支持 4.痛点 投入大效果差调优难 5.RAG应…

一、flask入门和视图

run启动参数 模板渲染 后端给前端页面传参 前端页面设置css from flask import Flask, render_template,jsonify# 创建flask对象 app = Flask(__name__)# 视图函数 + 路由route @app.route("/") def hello_world():# 响应,返回给前端的数据return "hello worl…

多 线 程

1&#xff0e;什么是多线程? 有了多线程&#xff0c;我们就可以让程序同时做多件事情 2.多线程的作用? 提高效率 3&#xff0e;多线程的应用场景? 只要你想让多个事情同时运行就需要用到多线程 比如:软件中的耗时操作、所有的聊天软件、所有的服务器 1.进程和线程【理解】 …

Day36|贪心算法part05:435. 无重叠区间、763.划分字母区间、56. 合并区间

435. 无重叠区间 有了上题射气球的因子&#xff0c;这题也就有思路了&#xff0c;反正无脑排序就行了&#xff1a; 首先将所有区间按照end的大小从小到大排序&#xff1b;选取最早end为起始x_end遍历所有区间&#xff0c;如果该区间的start比end大&#xff08;可重叠&#xf…

活动预告|如何构建云原生现代化数据栈?北京首场 Meetup 来啦!

数字化时代带来了海量的数据涌现&#xff0c;传统的数据架构已然无法满足现代企业的需求&#xff0c;现代化数据栈应运而生。基于云原生的现代化数据栈具备了多云兼容的特性&#xff0c;在不同的云环境下能够保持高性能运作&#xff0c;使企业得以无缝地处理和分析海量的数据集…

利用SARscape对日本填海造陆和天然气开采进行地表形变监测

日本千叶市&#xff0c;是日本南部重要的工业港市。位于西部的浦安市是一个典型的"填海造田"城市&#xff0c;东南部的东金区有一片天然气开采区域&#xff0c;本文利用SARscape&#xff0c;用干涉叠加的方法&#xff0c;即PS和SBAS&#xff0c;对这两个区域进行地表…

倒计时4天!百度Create AI开发者大会“大模型与深度学习技术”论坛亮点抢鲜看!

作为人工智能的核心基础技术&#xff0c;深度学习具有很强的通用性&#xff0c;大模型技术在深度学习的基础上&#xff0c;通过构建更加庞大神经网络模型和应用transformer等更加领先的算法&#xff0c;使模型的处理能力产生质的飞跃。飞桨&#xff08;PaddlePaddle&#xff09…

MySQL分区表(14/16)

分区表 基本概述 分区表是数据库中一种用于优化大型表数据管理和查询性能的技术。它将一个表的数据根据特定的规则或条件分割成多个部分&#xff0c;每个部分称为一个分区。每个分区可以独立于其他分区进行存储、管理和查询&#xff0c;这样可以提高数据处理的效率&#xff0…

VS Code中“@“符号如何自动补全导入路径

一、下载 Path Intellisense 插件 二、打开设置&#xff0c;在扩展中选择该插件&#xff0c;点击setting.json 三、添加配置&#xff1a; "":"${workspaceRoot}/src" 如图&#xff1a; 四、在项目src目录中新建jsconfig.json文件 &#xff08;一定要是src目…

动态规划(背包问题)

一:动态规划概述: 动态规划实际上是一种将原本的 大 方面的问题转化为许许多多的 小方面 的一种应用, 在一定程度上避免数据的重复, 并且能够将数据以自己希望的方式进行存储, 用来解决多阶段的数学问题, 从而提高算法的效率 在算法当中, 动态规划主要包括有: 递推, 线性DP 记忆…

【Java核心技术】第3章 Java的基本程序设计结构

1 数据类型 Java一共有8种数据类型&#xff1a; 4种整型 类型存储需求int4字节short2字节long8字节byte1字节 2种浮点型 类型存储需求float4字节double8字节 1种字符型 1种布尔型 2 变量声明 2.1 局部类型推断 如果可以从变量的初始值推断变量类型&#xff0c;只需要使用…

【数组】5螺旋矩阵

这里写自定义目录标题 一、题目二、解题精髓-循环不变量三、代码 一、题目 给定⼀个正整数 n&#xff0c;⽣成⼀个包含 1 到 n^2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的正⽅形矩阵。 示例: 输⼊: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ] 二、解题精髓…

JVM参数列表

-client :设置JVM使用client模式,特点启动较快(神机不明显(I5/8G/SSD)) -server :设置JVM使用server模式。64位JDK默认启动该模式 -agentlib:libname[options] :用于加载本地的lib -agentlib:hprof :用于获取JVM的运行情况 -agentpath:pathnamep[options] :加载制定路径的本…

Day:007(1) | Python爬虫:高效数据抓取的编程技术(scrapy框架使用)

Scrapy的介绍 Scrapy 是一个用于抓取网站和提取结构化数据的应用程序框架&#xff0c;可用于各种有用的应用程序&#xff0c;如数据挖掘、信息处理或历史存档。 尽管 Scrapy 最初是为网络抓取而设计的&#xff0c;但它也可用于使用API提取数据或用作通用网络爬虫。 Scrapy的优势…

【Nacos】Nacos最新版的安装、配置过程记录和踩坑分享

Nacos是什么&#xff1f;有什么功能&#xff1f;大家可以自行联网&#xff08;推荐 https://cn.bing.com/&#xff09;搜索&#xff0c;这里就不做介绍了。 简单的看了下官网&#xff0c;安装最新版的Nacos&#xff08;v2.3.2&#xff09;需要使用到JDK&#xff08;1.8.0&…