[toc]
ArkTS与C++的跨语言交互
Ohos 上的应用一般都是用 arkts 语言编写的,而 arkts 语言是无法直接调用 C/C++ 接口的,如果我们的应用需要调用 C/C++ 第三方库,需要在 arkts 和 C/C++ 之间建立一个可以互通的桥梁,Node-API 正是搭建这个桥梁的主要关键。
HarmonyOS 提供了 Node-API 框架用于实现 ArkTS/JS 与 C/C++ 模块之间的跨语言交互,Node-API规范封装了I/O、CPU密集型、OS底层等能力并对外暴露 ArkTS/JS 接口,从而实现ArkTS/JS和C/C++的交互。主要应用场景如下:
- 系统可以将框架层丰富的模块功能通过ArkTS/JS接口开放给上层应用。
- 应用开发者也可以选择将一些对性能、底层系统调用有要求的核心功能用C/C++封装实现,再通过ArkTS/JS接口使用,提高应用本身的执行效率。
架构组成
如上图所示,整体架构分为 C++、ArkTS和工具链三部分:
- C++:包含各种文件的引用、C++或者C代码、Native项目必需的配置文件等。
- ArkTS:包含界面UI、自身方法、调用引用包的方法等。
- 工具链:包含CMake编译工具在内的系列工具
C++ 部分中,.cpp
文件实现 C++ 方法,并通过NAPI
将C++方法与ArkTS方法关联,C++代码通过CMake编译工具编译成动态链接库so文件,使用index.d.ts
文件对外提供接口,ArkTS引入.so
文件后调用其中的接口,即可实现 ArkTS 对 C++ 对跨语言调用。
代码结构
1 | ├──entry/src/main |
我们需要在 hello.cpp
中通过 node-API
实现接口函数,并在 index.d.ts
中声明相应的接口函数,最后编译生成lbhello.so
,在 Index.ets
中导入 import lbhello.so
进行调用。
C++返回ArkTS的自定义类型
假如定义如下 ArkTS 类型:
1 | export interface weChatDailyType { |
我们希望 ArkTS 调用 C++ 接口函数返回类型为 weChatDailyType[]
的数组,以供内部需求。
首先,在文件 Index.d.ts
中声明接口:
1 | // entry/src/main/cpp/types/libentry/index.d.ts |
然后,在文件 napi_init.cpp
中配置模块描述信息,设置 Init 方法为 napi_module 的入口方法。__attribute__((constructor))
修饰的方法由系统自动调用,使用 NAPI 接口 napi_module_register()
传入模块描述信息进行模块注册,属性.nm_register_func
、.nm_modname
分别是模块初始化函数和模块名称,分别用于将我们写的 ArkTS 接口与具体的 C++ 实现进行绑定和映射,以及用作 ArkTS 侧引入的 so 库的名称,模块系统会根据后者的名称来区分不同的 so。
1 | // entry/src/main/cpp/napi_init.cpp |
注:以上代码无须复制,创建Native C++工程以后在 napi_init.cpp
代码中已配置好。
Init()
函数用于实现 ArkTS 接口与 C++ 实现函数的绑定与映射,napi_env env
用于提供 ArkTS 运行时的上下文,api_value exports
作为目标对象,接收通过 napi_property_descriptor
定义的属性,最后返回 exports
,确保初始化后的导出对象被正确设置.
napi_property_descriptor
定义了 C++ 暴露给 ArkTS 的属性和方法,在下面的示例代码中:
returnContacts
:暴露了一个名为returnContacts
的方法,对应的 C++ 实现是ReturnContacts
函数
1 | // entry/src/main/cpp/napi_init.cpp |
returnContacts
接口已经在文件 Index.d.ts
声明,我们需要实现该接口映射的 C++ 实现 ReturnContacts
:
1 | const std::vector<weChatDailyType> contacts = { |
函数主要功能是在 C++ 构造一个在 ArkTS 中可直接使用的 weChatDailyType 数组对象。对应 API 的介绍参考链接:>>>Node-API使用方法<<<
在接口配置文件 oh-package.json5
中将 index.d.ts
与CMake编译的so文件关联起来。模块级目录下oh-package.json5
文件添加so文件依赖。
1 | // index.d.ts |
上面所述流程完成后,编译,我们即可在 ArkTS 进行调用:
1 | import libentry from 'libentry.so' |
libentry.so
是 cpp 编译后的 so 文件名。
>>>实现使用NAPI调用C标准库的功能<<<
>>>使用Node-API实现跨语言交互<<<
C++解析ArkTS的类型
基本类型
NAPI 提供了相应的接口将 ArkTS 的基本类型转换为 C++ 的基本类型:
napi_get_value_uint32
:将ArkTS环境中number类型数据转为Node-API模块中的uint32类型数据。napi_get_value_int32
:将ArkTS环境中获取的number类型数据转为Node-API模块中的int32类型数据。napi_get_value_int64
:将ArkTS环境中获取的number类型数据转为Node-API模块中的int64类型数据。napi_get_value_double
:将ArkTS环境中获取的number类型数据转为Node-API模块中的double类型数据。napi_create_int32
:将Node-API模块中的int32_t类型转换为ArkTS环境中number类型。napi_create_uint32
:将Node-API模块中的uint32_t类型转换为ArkTS环境中number类型。napi_create_int64
:将Node-API模块中的int64_t类型转换为ArkTS环境中number类型。napi_create_double
:将Node-API模块中的double类型转换为ArkTS环境中number类型。
示例,解析从 ArkTS 获取的 uint32 类型值:
1 |
|
string 字符串
NAPI 提供了相应的接口将 ArkTS 的字符串类型转换为 C++ 的字符串类型:
- napi_get_value_string_utf8: 需要将ArkTS的字符类型的数据转换为utf8编码的字符时使用这个函数。
- napi_create_string_utf8: 需要通过UTF8编码的C字符串创建ArkTS string值时使用这个函数。
- napi_get_value_string_utf16: 需要将ArkTS的字符类型的数据转换为utf16编码的字符时使用这个函数。
- napi_create_string_utf16: 需要通过UTF16编码的C字符串创建ArkTS string值时使用这个函数。
- napi_get_value_string_latin1: 需要将ArkTS的字符类型的数据转换为ISO-8859-1编码的字符时使用这个函数。
- napi_create_string_latin1: 需要通过ISO-8859-1编码的字符串创建ArkTS string值时使用这个函数。
对字符串对象的处理相比基本类型稍微复杂些,需要通过 napi_get_value_string_utf8 将 ArkTS 的字符串对象转换为 C++ 的 std::string 对象,但需要传入字符串的长度,napi_get_value_string_utf8
的原型如下:
1 | napi_status napi_get_value_string_utf8(napi_env env, |
- napi_value value:要转换的 ArkTS 字符串值
- char* buf:目标缓冲区,用于存储转换后的 UTF-8 字符串
- size_t bufsize:目标缓冲区的大小(以字节为单位)
- size_t* result:可选参数,用于存储转换后的字符串长度(不包括终止符)
函数返回 napi_status
枚举类型,参考:napi_status 类型说明
我们需要先创建一个 char 类型的空间去接收转换后的字符串,创建 char 数组需要指定大小,可以先调用一次napi_get_value_string_utf8
,buf传入空获取到传入的数据大小,然后创建对应大小buf,再次调用napi_get_value_string_utf8
获取转换后的字符串,示例如下:
1 |
|
object 类型
解析object对象参数和前面参数一样,通过 napi_get_cb_info
转换为 napi_value
对象后,通过 napi_get_named_property
获取对象中的属性值:
1 | //arkts对象: |
数组类型
和 string 类似,数组类型参数需要先通过 napi_get_array_length
参数获取数组长度,再通过 napi_get_element
获取对应每个 napi_value
对象,通过上述基本类型转换为对应C++对象:
1 | napi_value Demo::setArrays(napi_env env, napi_callback_info info){ |
ArrayBuffer 类型
如果ArkTS向C++传输二进制流,需要用到 ArrayBuffer
类型接收,在C++侧通过 napi_get_arraybuffer_info
转换成C++字节流,接口说明:
1 | napi_status napi_get_arraybuffer_info(napi_env env, |
- napi_value arraybuffer:要查询的 ArkTS ArrayBuffer 对象
- void** data:输出参数,指向 ArrayBuffer 底层数据的指针
- size_t* byte_length:输出参数,指向 ArrayBuffer 的字节长度
第三个参数传入空时,只会获取字节流大小。第三个参数是指向指针的指针,我们不需要创建空间,函数内部会创建空间:
1 | napi_value Demo::setArrayBufferData(napi_env env, napi_callback_info info){ |
综合案例
最后附上一个综合案例,包含大部分的类型转换:
>>>北向应用集成三方库——应用如何调用C/C++三方库<<<