OpenHarmony开发实战:简易Native C++ 示例(ArkTS)
蜀道山QAQ 2024-07-20 08:35:03 阅读 63
本篇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。
硬件要求
开发板类型:润和RK3568开发板。OpenHarmony系统:3.2 Release。
环境搭建
完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:
获取OpenHarmony系统版本:标准系统解决方案(二进制)。以3.2 Release版本为例:
搭建烧录环境。
完成DevEco Device Tool的安装完成RK3568开发板的烧录
搭建开发环境。
开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”)。工程创建完成后,选择使用真机进行调测。
代码结构解读
本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在gitee中提供。
使用Native C++模板创建项目会自动生成cpp文件夹、types文件夹、CMakeList.txt文件,开发者可以根据实际情况自行添加修改其他文件及文件夹。
<code>├──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侧操作详解
配置模块描述信息,设置Init方法为napi_module的入口方法。__attribute__((constructor))修饰的方法由系统自动调用,使用NAPI接口napi_module_register()传入模块描述信息进行模块注册。Native C++模板创建项目会自动生成此结代码,开发者可根据实际情况修改其中内容。
<code>// 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);
}
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;
}
本例中使用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;
}
添加接口文件以及接口配置文件。接口文件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"
}
}
在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容器组件进行布局。
<code>// 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)
}
}
最后
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。
这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。
希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
如果你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感兴趣以及转行人员,可以直接领取这份资料
获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
鸿蒙(HarmonyOS NEXT)最新学习路线
HarmonOS基础技能
HarmonOS就业必备技能
HarmonOS多媒体技术
鸿蒙NaPi组件进阶
HarmonOS高级技能
初识HarmonOS内核
实战就业级设备开发
有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
《鸿蒙 (OpenHarmony)开发入门教学视频》
《鸿蒙生态应用开发V2.0白皮书》
《鸿蒙 (OpenHarmony)开发基础到实战手册》
OpenHarmony北向、南向开发环境搭建
《鸿蒙开发基础》
ArkTS语言安装DevEco Studio运用你的第一个ArkTS应用ArkUI声明式UI开发.……
《鸿蒙开发进阶》
Stage模型入门网络管理数据管理电话服务分布式应用开发通知与窗口管理多媒体技术安全技能任务管理WebGL国际化开发应用测试DFX面向未来设计鸿蒙系统移植和裁剪定制……
《鸿蒙进阶实战》
ArkTS实践UIAbility应用网络案例……
获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料
总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。