OpenHarmony开发实战指南:简易Native C++示例教程(ArkTS)

2024-12-18 0 773

软件开发过程中,实现不同功能和编程语言之间的交互往往既具挑战性又充满可能。以C++模板为例,它能够通过NAPI接口调用C语言的标准库,这一做法不仅技术含量高,而且颇具吸引力。

OpenHarmony开发实战指南:简易Native C++示例教程(ArkTS)

OpenHarmony开发实战指南:简易Native C++示例教程(ArkTS)

开发环境搭建准备

搭建开发环境是首要任务。以开发板为例,这个过程有固定的操作步骤。就好比建造房屋需要打好地基,这个过程在特定的开发环境中进行,主要参与者是开发者。开发者们会根据各自的进度和需求对步骤进行适当调整,因为不同的开发板可能存在细微的差别。每一步都必须精确无误,任何小错误都可能影响后续功能的实现。此外,针对不同的开发板,我们可能需要查阅不同的资料或借鉴基于该开发板的过往开发经验,以确保环境搭建的完善。

├──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               // 资源文件目录

在进行环境搭建的流程中,我们必须牢记精确度至关重要。这就如同依照地图上的路径前行,一旦踏错一步,就可能失去方向。

OpenHarmony开发实战指南:简易Native C++示例教程(ArkTS)

工程创建选择

OpenHarmony开发实战指南:简易Native C++示例教程(ArkTS)

配置好开发环境后,便进入工程创建阶段。在这个阶段,我们选择了“空”模板。这个选择并非随意,就好比挑选建房子的模板,不同的模板意味着不同的功能和操作步骤。我们需要根据项目最终目标来决定。比如,若目标是构建一个简单灵活的基础框架,那么这个模板就很合适。创建工程会自动生成一系列文件夹和文件,比如C++模板会生成cpp文件夹、types文件夹和.txt文件。开发者需要在这个基础框架上进一步细化,根据实际情况添加或修改文件和文件夹内容。这是一个既需要细心又需结合实际需求的过程。

// 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);
}

应用架构剖析

整个项目的核心结构是应用架构,它由三部分组成:C++、ArkTS和工具链。理解这三部分的功能及其相互关系至关重要。以ArkTS调用C++方法为例,并非直接调用。这里NAPI、CMake等工具起到桥梁作用,就像外交官在不同国家间沟通协调。没有这些工具,交互难以顺畅。各部分间的关联关系有明确的图示,对开发者而言,就像有航海图指引,能避免迷失方向。C++代码由CMake编译成动态链接库so文件,index.d.ts文件负责提供接口,这一系列步骤确保了不同部分间信息交流和功能调用的顺利进行。

// 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++代码编译处理

// hello.cpp
#include 
#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
export const myHypot: (a: number, b: number) => number;

使用CMake工具编译C++代码生成so文件是项目中的关键步骤。这个过程并不简单,包含众多细节。特别是那些ArkTS端需要引入的C++代码,在生成so文件后,其在项目框架中的融合程度和兼容性至关重要。编译过程中,还包括注册模块等操作,比如括号内的方法会被系统自动调用,通过NAPI接口输入模块描述信息来完成模块注册。Init方法作为C++模板的一种结构,并非固定不变,开发者可以根据需要对其进行调整,就像对房屋的基本框架进行个性化的装修一样。

// oh-package.json5
{
  "name": "libhello.so",
  "types": "./index.d.ts"
}
// entry/oh-package.json5
{
  "devDependencies": {
    "@types/libhello.so": "file:./src/main/cpp/types/libhello"
  }
}

引入C标准库操作

# 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)

在应用这个项目时,加入C标准库是个关键步骤。比如,使用math.h头文件,这是开发者使用C标准库功能的必经之路。在此过程中,正确解析传入参数的类型至关重要。比如,使用hypot方法计算两个数的平方和再开平方根,这就需要在确保类型解析无误的情况下进行。每个函数的调用都像是按下一个按钮,若顺序或参数出错,机器便无法正常工作。

// 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);
        }
      })
  }
}

界面实现与事件触发

OpenHarmony开发实战指南:简易Native C++示例教程(ArkTS)

Index.ets文件界面已经实现,通过Row容器组件来布局。这是将之前的编程逻辑和功能直观展示给用户的关键环节。就像房屋内部装修完毕后,开始进行家具布置。同时,为组件添加点击事件也很关键,用户点击按钮时,会触发事件并调用.so文件提供的方法进行计算,并给出结果。这一过程涉及前后端对接,任何一个环节出现错误都可能导致用户交互失败。

// 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)
  }
}

那么,你是否在自己的项目或了解的项目中遇到过语言间交互或调用出现错误的问题?欢迎在评论区留言交流。若觉得本文对您有所帮助,不妨点赞并转发。

OpenHarmony开发实战指南:简易Native C++示例教程(ArkTS)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

七爪网 行业资讯 OpenHarmony开发实战指南:简易Native C++示例教程(ArkTS) https://www.7claw.com/2803597.html

七爪网源码交易平台

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务