使用 DevEco 一键生成 ArkTS 调用 C++ 的胶水代码

为什么需要 ArkTS 调用 C++?

在一些对性能要求较高的场景下,比如:

  • AI 推理等大运算处理
  • 文件处理性能要求高、大文件处理、复杂文件操作
  • 低延时实时通信
    Native 侧比 ArkTS 有更高的效率和更快的响应速度,并且 Native 侧开发提供了更高的灵活性。而 Native 侧特指应用在设备上直接通过 C/C++ 语言编写的底层代码,这些代码可以直接访问控制设备的硬件资源,比如:CPU、内存、网络、设备外设等。

因此在这些场景下,需要 ArkTS 调用 C++ 代码,以充分发挥 Native 侧的强大性能和灵活性。

DevEco IDE 生成 ArkTS 调用 C++ 的胶水代码

ArkTS 在调用 C++ 代码时,需要生成胶水代码。DevEco IDE 提供了一键生成胶水代码的功能,帮助开发者快速生成胶水代码。

创建 Demo 工程

  1. 打开 DevEco IDE,选择 File > New > Create Project。
  2. 在弹出的新建工程界面中,选择 Native C++ 模板,点击 Next。
  3. 配置工程的名称(比如我的项目名 ArkTSCallCPP、路径等信息后,点击 Finish 即可创建工程。

在这里插入图片描述

生成胶水代码

entry > src > main 路径下,可以看到已经创建了 cpp 目录,并生成了胶水代码框架文件 napi_init.cppCMakeLists.txt,只有在 cpp 路径下存在 CMakeLists.txt 的情况下,才能进行下面步骤。

编写 C++ 代码

  1. cpp 目录下创建 MyTime.h,分别编写 C++ 代码,如我创建了 MyTime class 并声明了 now() 方法:
#ifndef ARKTSCALLCPP_MYTIME_H
#define ARKTSCALLCPP_MYTIME_H

#include <string>
#include <ctime>

using namespace std;

class MyTime {
public:
    explicit MyTime(){};
    virtual ~MyTime(){};

    string now() {
        time(&this->nowTime);
        return string(ctime(&this->nowTime));
    };

private:
    time_t nowTime;
};

#endif // ARKTSCALLCPP_MYTIME_H
  1. 为 C++ 类生成胶水代码,在 MyTime.h 文件中(头文件支持类型:.hpp,.hxx,.hh,.h),光标放在 MyTime 类上并右键选择 Generate > NAPI,快速生成当前函数或类的胶水代码函数框架。

在这里插入图片描述

  1. napi_init.cpp 文件中TODO位置,补充相应的功能实现代码。补充方法 参考

在这里插入图片描述

下面是我的代码实现,主要完成 Ts 对象到 C++ 对象转换,以及 MyTime::now() 方法的实现:

// string -> napi value
static napi_value string2Value(napi_env env, string str) {
    int length = str.length();

    napi_value output_string;
    napi_create_string_utf8(env, str.c_str(), length, &output_string);

    return output_string;
}

// now 方法
static napi_value NAPI_Class_MyTime_now(napi_env env, napi_callback_info info) {
    size_t argc = 0;
    napi_value ts_class_obj = nullptr;

    // Get detailed information about the function call, such as input parameters.
    napi_get_cb_info(env, info, &argc, nullptr, &ts_class_obj, nullptr);

    MyTime *c_class_obj = nullptr;
    // Convert ArkTS object to C++ object
    napi_unwrap(env, ts_class_obj, (void **)&c_class_obj);

    // Get parameters passed by ArkTS
    // 这里 now 方法没有参数,所以省略

    return string2Value(env, c_class_obj->now());
}

// TS object -> C++ object
static napi_value NAPI_Constructor_MyTime(napi_env env, napi_callback_info info) {
    // Create Napi object
    napi_value ts_class_obj;
    size_t argc = 0;

    // Get detailed information about the function call, such as input parameters.
    napi_get_cb_info(env, info, &argc, nullptr, &ts_class_obj, nullptr);

    // Create C++ object
    MyTime *c_class_obj = new MyTime();

    // Set the JS object hintStr attribute
//     napi_set_named_property(env, ts_class_obj, "nowTime", nullptr);

    // Binding JS objects with C++ objects
    napi_wrap(
        env, ts_class_obj, c_class_obj,
        // Define callback function for recycling JS objects, used to destroy C++ object
        [](napi_env env, void *finalize_data, void *finalize_hint) {
            MyTime *c_class_obj = (MyTime *)finalize_data;
            delete c_class_obj;
            c_class_obj = nullptr;
        },
        nullptr, nullptr);
    return ts_class_obj;
}

ArkTS 调用 C++

通过前面的步骤,可以在 entry/src/main/cpp/types/libentry/Index.d.ts 文件中,看到导出的 MyTime 类,以及 now() 方法。

  1. entry/src/main/ets/pages/Index.ets 文件中,引入 MyTime 类:
import { MyTime } from 'libentry.so';
  1. 创建 MyTime 对象,并调用 now() 方法:
@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  aboutToAppear(): void {
    let time = new MyTime();
    this.message = time.now();
  }

  build() {
    RelativeContainer() {
      Text() {
        Span("来自C++的消息:\n")
        Span(this.message)
          .fontColor(Color.Blue)
      }
      .id('HelloWorld')
      .fontSize(25)
      .fontWeight(FontWeight.Bold)
      .alignRules({
        center: { anchor: '__container__', align: VerticalAlign.Center },
        middle: { anchor: '__container__', align: HorizontalAlign.Center }
      })
    }
    .height('100%')
    .width('100%')
  }
}
  1. 运行项目,可以看到效果:

在这里插入图片描述

快速生成 C++ 函数定义

当前支持在跨语言的d.ts文件中,通过Generate native implementation功能,一键生成C++文件中对应函数定义。

将光标悬浮在未定义的函数名处,在悬浮窗中点击Generate native implementation,或点击页面上出现的红色灯泡图标,选择Generate native implementation,生成函数定义。

在这里插入图片描述

总结

跨语言调用 C++ 代码,可以提高程序运行效率,提高开发灵活性,同时也可以方便地实现一些复杂的业务逻辑。其它开发语言、框架,也都有对 C++ 的跨语言调用支持,比如 golang 的 CGO 等,但是鸿蒙 DevEco IDE 提供了方便的一键生成功能,减少了重复代码的编写工作。

参考

Native侧调用HAR/HSP模块接口
跨语言代码编辑
跨语言调用复杂参数传递开发实践
Node-API
CrossModuleReference

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐