头图

一文了解HarmonyOS Next ArkTS与C++数据类型转换

背景

HarmonyOS的主力开发语言是ArkTS,也提供了C++语言的支持,对于一些能力,比如音视频编解码等,HarmonyOS 提供的也只有C++ API,对于一些其他平台现有能力的迁移,C++也是最快捷高效的,所以对于一个HarmonyOS 开发者,掌握ArkTS与C++交互成了一项必备技能。

每种编程语言都有自己定义的数据类型,不同编程语言之间互相调用就涉及到了数据类型的转换,ArkTS与C++的转换主要有Node-API接口提供,本文介绍ArkTS与C++互相转换的接口和最佳实践。

做过Android JNI开发的小伙伴对Java和C++的互相调用有所了解,JNI提供了Java和C++的类型转换,与JNI不同的是NAPI中,TS调用C++的参数都封装到了一起:

static napi_value add(napi_env env, napi_callback_info info)  
{
}

不管要传递多少个参数,都封装在napi_callback_info中,可以从napi_callback_info中获取全部参数信息:

size_t argc = 7;//参数个数
napi_value args[7] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

接下来就可以从args中分别读取ArkTS传来的参数信息,下面介绍如何进行类型转换。

ArkTS转C++类型

TS基本数据类型:

  • 数字类型 number
  • 字符串类型 string
  • 布尔类型 boolean
  • 任意精度整形 bigint
  • 对象类型 object
    C++基本数据类型:
  • 整形

    • int
    • short
    • long
    • long long
  • 浮点型

    • float
    • double
    • long double
  • 字符型 char
  • 布尔型 bool

基本数据类型转换

NAPI提供了上面两种语言对应的类型的转换:

  • 转换为布尔类型:napi_get_value_bool
  • 转换为int32:napi_get_value_int32
  • 转换为int64:napi_get_value_int64
  • 转换为无符号32位:napi_get_value_uint32
  • 转换为double:napi_get_value_double
  • bitint 64位:napi_get_value_bigint_int64
  • bitint 无符号64位:`napi_get_value_bigint_uint64
    除了bool类型,其他基本类型就是数值类型,TS中的数值类型对应C++各种细分类型,分别调用上面不同函数即可,调用方式:

    int intValue;
    napi_get_value_int32(env, args[0], &intValue);

    上面代码通过napi_get_value_int32将TS中的number转换为int赋值给intValue变量。

字符串类型转换

对于字符串和object对象处理稍微复杂些,通过napi_get_value_string_utf8将js的字符串对象转换为c++的std::string对象。但是要创建std:string需要先知道TS中传来的字符串的长度,看napi_get_value_string_utf8函数说明:

napi_status napi_get_value_string_utf8(napi_env env,
                                       napi_value value,
                                       char* buf,
                                       size_t bufsize,
                                       size_t* result)
  • [in] env: The environment that the API is invoked under.
  • [in] valuenapi_value representing JavaScript string.
  • [in] buf: Buffer to write the UTF8-encoded string into. If NULL is passed in, the length of the string in bytes and excluding the null terminator is returned in result.
  • [in] bufsize: Size of the destination buffer. When this value is insufficient, the returned string is truncated and null-terminated.
  • [out] result: Number of bytes copied into the buffer, excluding the null terminator.

Returns napi_ok if the API succeeded. If a non-string napi_value is passed in it returns napi_string_expected.

我们需要先创建一个char类型的空间去接收转换后的字符串,创建char空间需要指定大小,可以先调用一次napi_get_value_string_utf8,buf传入空获取到传入的数据大小,然后创建对应大小buf,再次调用napi_get_value_string_utf8获取转换后的字符串:

void JsValueToString(const napi_env &env, const napi_value &value, std::string &target) {  
    size_t result = 0;  
    napi_get_value_string_utf8(env, value, nullptr, 0, &result);  
    std::unique_ptr<char[]> buf(new char[result+1]);  
    if (buf.get() == nullptr) {  
        return;  
    }  
    (void)memset(buf.get(), 0, result+1);  
    napi_get_value_string_utf8(env, value, buf.get(), result+1, &result);  
    target = buf.get();  
}

上面方法就是封装了一个转换字符串对应的工具函数。

object类型转换

解析object对象参数和前面参数一样,通过napi_get_cb_info转换为napi_value对象后,通过napi_get_named_property获取对象中的属性值:

//ts对象:
export class Task {  
  
  public id: number; //unique task identify  
  public channel?: number;   
  //...
}


napi_value Demo::startTask(napi_env env, napi_callback_info info){
    size_t argc = 1;
    napi_value js_cb;
    napi_get_cb_info(env, info, &argc, &js_cb, nullptr, nullptr);


    napi_value taskIdNapiValue;
    napi_get_named_property(env, js_cb, "id", &taskIdNapiValue);
    int32_t taskid;
    napi_get_value_int32(env, taskIdNapiValue, &taskid);


    napi_value channelSelectNapiValue;
    napi_get_named_property(env, js_cb, "channelSelect", &channelSelectNapiValue);
    int32_t channel_select;
    napi_get_value_int32(env, channelSelectNapiValue, &channel_select);

    //....

    //调用业务逻辑
    return nullptr;
}

数组类型类型转换

和string类似,数组类型参数需要先通过napi_get_array_length参数获取数组长度,再通过napi_get_element获取对应每个napi_value对象,通过上述基本类型转换为对应C++对象:

napi_value Demo::setArrays(napi_env env, napi_callback_info info){
    std::vector<std::string> backupip_list;

    size_t argc = 1;
    napi_value args[1] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    bool is_array;
    //判断是否为数组
    napi_status status = napi_is_array(env, args[0], &is_array);
    if (!is_array) {
        return nullptr;
    }
    napi_value array = args[0];
    uint32_t length;
    napi_get_array_length(env,array,&length);
    for(int i = 0; i < length; i++){
        napi_value element;
        napi_get_element(env, array, i, &element); // 获取返回值数组的每个元素
        std::string ipStr;
        NapiUtil::JsValueToString(env, element, ipStr);
        backupip_list.push_back(ipStr);
    }   
    //调用业务逻辑
    return nullptr;
}

ArrayBuffer类型转换

如果TS向C++传输二进制流,需要用到ArrayBuffer类型数据,在C++侧通过napi_get_arraybuffer_info转换成C++字节流,接口说明:

napi_status napi_get_arraybuffer_info(napi_env env,
                                      napi_value arraybuffer,
                                      void** data,
                                      size_t* byte_length)
  • [in] env: The environment that the API is invoked under.
  • [in] arraybuffernapi_value representing the ArrayBuffer being queried.
  • [out] data: The underlying data buffer of the ArrayBuffer. If byte_length is 0, this may be NULL or any other pointer value.
  • [out] byte_length: Length in bytes of the underlying data buffer.
    Returns napi_ok if the API succeeded.
    This API is used to retrieve the underlying data buffer of an ArrayBuffer and its length.

第三个参数传入空时,只会获取字节流大小。第三个参数是指向指针的指针,我们不需要创建空间,函数内部会创建空间:

napi_value Demo::setArrayBufferData(napi_env env, napi_callback_info info){
    size_t argc = 1;
    napi_value js_cb;
    napi_get_cb_info(env, info, &argc, &js_cb, nullptr, nullptr);

    // 获取 ArrayBuffer 对象的指针和长度 
    void* buffer; 
    size_t length; 
    napi_get_arraybuffer_info(env, arrayBuffer, &buffer, &length); 
    // 打印 ArrayBuffer 中的数据 ,也可以修改ArrayBuffer的值
    uint32_t* data = (uint32_t*) buffer;

    //调用业务逻辑
    return nullptr;
}

C++类型转ArkTS 类型

上面介绍ArkTS转C++时看到的示例代码一般返回nullptr,如果要返回具体类型的数据,不能直接返回C++类型的变量,也需要转换成TS类型变量,NAPI提供了napi_value类型,表示TS类型数据,创建对应TS类型变量的函数:

  • napi_create_uint32
  • napi_create_int64
  • napi_create_double
  • napi_create_bigint_int64
  • napi_create_bigint_uint64
  • napi_create_bigint_words
  • napi_create_string_latin1
  • node_api_create_external_string_latin1
  • napi_create_string_utf16
  • node_api_create_external_string_utf16
  • napi_create_string_utf8
  • napi_get_boolean
    使用示例:

    napi_value Demo::hasSon(napi_env env, napi_callback_info info){
      size_t argc = 1;
      napi_value args[1] = {nullptr};
      napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
      int id;
      napi_get_value_int32(env, args[0], &id);
      napi_value result;
      bool hasSonById = _HasSon(id);
      //创建布尔型js对象
      napi_get_boolean(env, hasSonById, &result);
      return result;
    }

总结

本文介绍了C++与TS各自数据类型与互相之间的数据类型转换,在需要使用C++模块时可以快速上手对各种数据类型进行转换。


轻口味
28.1k 声望4.5k 粉丝

移动端十年老人,主要做IM、音视频、AI方向,目前在做鸿蒙化适配,欢迎这些方向的同学交流:wodekouwei