AKI

简介

AKI (Alpha Kernel Interacting) 是一款简化Node-API(C/C++插件)语法的ArkTs FFI开发框架,针对OpenHarmony Native开发提供ETS与C/C++跨语言访问场景解决方案。支持极简语法糖使用方式,一行代码完成ETS与C/C++的无障碍跨语言互调,所键即所得。

效果展示

写同样功能的代码

  • napi代码
    napi_HelloWorld

  • aki代码
    aki_HelloWorld

  • 框架展示


下载与安装

依赖配置(2选1)

  • 源码依赖(推荐)

    指定cpp路径下(如:项目根路径/entry/src/main/cpp)

    cd entry/src/main/cpp
    git clone https://gitcode.com/openharmony-sig/aki.git
    

    CMakeLists.txt添加依赖(假定编译动态库名为:libhello.so):

    add_subdirectory(aki)
    target_link_libraries(hello PUBLIC aki_jsbind)
    
  • ohpm har包依赖

    指定路径下(如:项目根路径/entry),输入如下命令安装ohpm har包依赖

    cd entry
    ohpm install @ohos/aki
    

    CMakeLists.txt添加依赖(假定编译动态库名为:libhello.so):

    set(AKI_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules/@ohos/aki) # 设置AKI根路径
    set(CMAKE_MODULE_PATH ${AKI_ROOT_PATH})
    find_package(Aki REQUIRED)
    
    ...
    
    target_link_libraries(hello PUBLIC Aki::libjsbind) # 链接二进制依赖 & 头文件
    

C++ 标准兼容性

AKI 同时支持 C++17C++11默认开启 C++17。由于 AKI 的核心实现大量依赖 C++ 模板元编程(如 napi_value.hnapi_value_trait.h 等头文件),C++ 标准设置不仅影响 AKI 自身编译,也会通过头文件引入影响用户工程的编译。在源码依赖和 HAR 包依赖两种模式下,C++ 标准和宏定义(AKI_ENABLE_CXX_STANDARD_11=1)均通过 CMake 属性自动传播到链接 AKI 的用户目标,无需额外配置。

切换到 C++11 模式,在项目的 build-profile.json5(如:项目根路径/entry/build-profile.json5)中添加编译参数:

{
    "apiType": "stageMode",
    "buildOption": {
        "externalNativeOptions": {
            "path": "./src/main/cpp/CMakeLists.txt",
            "arguments": "-DAKI_ENABLE_CXX_STANDARD_11=ON",
            "cppFlags": "",
        }
    },
    "targets": [
        {
            "name": "default"
        }
    ]
}

注意

  • OHPM 上发布的官方 @ohos/aki HAR 包为 C++17 预编译产物。若需通过 HAR 方式使用 C++11,需自行从源码构建 C++11 版本的 HAR(在 HAR 的 build-profile.json5 arguments 中添加 -DAKI_ENABLE_CXX_STANDARD_11=ON,然后重新构建打包)。
  • C++11 模式下,std::optional<T> 类型转换不可用(即 ETS 与 C/C++ 之间的 Optional 类型映射,以及相关的 JSBIND_PROPERTY optional 属性绑定)。其余所有 AKI 功能(全局函数、类绑定、枚举绑定、回调、线程安全函数、TaskRunner、AsyncWorker、aki::Value 等)均通过内部 C++11 降级实现保持完全可用。
  • AKI 测试套件已覆盖 C++11 模式验证:optional 相关测试在 C++11 模式下跳过,其余功能测试均可通过。

约束与限制

兼容性

在下述版本验证通过

  • DevEco Studio NEXT Release: 5.0.3.900, SDK: API12(Command Line Tools 5.0.3.900)
  • DevEco Studio: 4.0.0.400, SDK: API10(4.0.9.6)
  • DevEco Studio: 3.1.0.500, SDK: API9(3.2.12.2)

权限要求

使用示例

简单示例

Native C/C++ 侧代码
#include <string>
#include <aki/jsbind.h>

std::string HelloWorld()
{
    return "Hello World";
}

JSBIND_GLOBAL()
{
    JSBIND_FUNCTION(HelloWorld);
}

JSBIND_ADDON(ModuleName);
ArkTS 侧代码
import libaki from "lib<ModuleName>.so"

console.log(libaki.HelloWorld());

补充示例

Native C/C++ 侧代码
#include <string>
#include <aki/jsbind.h>

// 类/结构体
class Person {
public:
    Person(std::string n,int a, double w)
    : name(n), age(a), weight(w) {};
    std::string SayHello() {
      return "HELLO AKI";
    }
    std::string name;
    int age;
    double weight;
};

// 全局函数
Person MakePerson() {
    Person person("aki", 99, 128.8);
    return person;
}

// AKI JSBind 语法糖
JSBIND_GLOBAL() {
    JSBIND_FUNCTION(MakePerson);
}
JSBIND_CLASS(Person) {
    JSBIND_CONSTRUCTOR<std::string, int, double>();
    JSBIND_METHOD(SayHello);
    JSBIND_PROPERTY(name);
    JSBIND_PROPERTY(age);
    JSBIND_PROPERTY(weight);
}
JSBIND_ADDON(ModuleName);
ArkTS 侧代码
import libaki, {Person} from "lib<ModuleName>.so"

// 调用 C/C++ Person 构造函数
let P1:Person = new Person("aki", 10 , 100);

// 访问类/结构体成员属性
console.log(P1.name + " " + P1.age + " " + P1.weight);

// 调用类/结构体成员函数
P1.SayHello();

// 调用 C/C++ 全局函数
let P2:Person = libaki.MakePerson();

// 极简使用,支持全类型转换
console.log(P2.name + " " + P2.age + " " + P2.weight);

注意事项

所有通过aki绑定的函数、类、类属性等,与Node-API规则一致,都需要写到对应的导出文件中,比如index.d.ts

使用说明

⚠️ 重要提示:一个APP中,多个模块或者多个依赖项引入aki,需要保证各个aki版本一致,因为APP运行时各模块在同一进程中共享符号表,不同的aki版本加载的API符号可能会不一致,会出现各种crash。

  1. 了解ETS和C/C++两者语言之间的数据类型映射
  2. 了解如何通过AKI,让ETS和C/C++之间建立联系的方法

数据类型映射表

ETS C/C++
Boolean bool
参考
Number uint8_t, int8_t, uint16_t, int16_t, short, int32, int64, uint32_t, uint64_t, float, double, enum
参考
String const char*, std::string
参考
Array std::vector<T>, std::array<T, N>
参考
Function std::function<R (P...)>
aki::Callback<R (P...)>
aki::SafetyCallback<R (P...)>
参考
Class Object class
JsonObject std::map<std::string,T>
参考
Map/HashMap/HashSet std::map<K,T>, std::unordered_map<K,T>, std::unordered_set<T>
参考
ArrayBuffer,
TypedArray
aki::ArrayBuffer
参考
Promise JSBIND_PFUNCTION, JSBIND_PMETHOD
Optional std::optional<T>
参考
any aki::Value, napi_value

NOTE: 带有阴影部分的表示已支持

const char* 是以引用方式传递参数,如遇到异步操作,请使用传值方式:std::string

Boolean

如下示例,开发者直接声明函数入参及返回值类型,使用AKI绑定后,框架自适应对 C/C++ 的 bool 及 ETS 的 Boolean 类型进行转化。

示例:

  • C++

    #include <aki/jsbind.h>
    bool Foo(bool flag) {
      ...
      return true;
    }
    JSBIND_GLOBAL() {
        JSBIND_FUNCTION(Foo, "foo");
    }
    JSBIND_ADDON(hello)
    
  • ETS

    import libAddon from 'libhello.so'
    let flag = libAddon.foo(true);
    

Number

如下示例,开发者直接声明函数入参及返回值类型,使用AKI绑定后,框架自适应对 C/C++ 的 uint8_t, int8_t, uint16_t, int16_t, short, int32, int64, uint32_t, uint64_t, float, double, enum 及 ETS 的 Number 类型进行转化。

  • float: 浮点型转换时存在精度丢失,对于高精度场景,请使用 double
  • enum: 枚举类型的转化请参考

示例:

  • C++

    #include <aki/jsbind.h>
    int Foo(int num) {
      ...
      return 666;
    }
    JSBIND_GLOBAL() {
        JSBIND_FUNCTION(Foo, "foo");
    }
    JSBIND_ADDON(hello)
    
  • ETS

    import libAddon from 'libhello.so'
    let num = libAddon.foo(888);
    

String

如下示例,开发者直接声明函数入参及返回值类型,使用AKI绑定后,框架自适应对 C/C++ 的 const char*, std::string 及 ETS 的 String 类型进行转化。

  • const char* 是以引用方式传递参数,如遇到异步操作,请使用传值方式:std::string;

示例:

  • C++

    #include <aki/jsbind.h>
    std::string Foo(const char* c_str, std::string str) {
      ...
      return "AKI 666";
    }
    JSBIND_GLOBAL() {
        JSBIND_FUNCTION(Foo, "foo");
    }
    JSBIND_ADDON(hello)
    
  • ETS

    import libAddon from 'libhello.so'
    let str = libAddon.foo("AKI", "666");
    

Array

如下示例,开发者直接声明函数入参及返回值类型,使用AKI绑定后,框架自适应对 C/C++ 的 std::vector<T>, std::array<T, N> 及 ETS 的 [] 类型进行转化。

  • 数组类型仅支持同种类型的数组声明;
  • aki暂不支持 std::vector<class>。替代方案为使用 std::vector<aki::Value> 接收ArkTs侧传下来的 Array<class>

示例:

  • C++

    #include <aki/jsbind.h>
    std::vector<double> Foo(std::array<int, 3>) {
      std::vector<double> result;
      ...
      return result;
    }
    JSBIND_GLOBAL() {
        JSBIND_FUNCTION(Foo, "foo");
    }
    JSBIND_ADDON(hello)
    
  • ETS

    import libAddon from 'libhello.so'
    let array = libAddon.foo([1, 2, 3]);
    

Map/HashMap/HashSet

AKI 支持使用 C/C++ 的 std::map<K, T>, std::unordered_map<K, T>, std::unordered_set<T> 映射 ETS 的 Map/HashMap/HashSet 类型。

  • Map/HashMap 支持的键值类型为:std::string, int, long, aki::Value
  • HashSet 支持的元素类型为:int, long, std::string, aki::Value
  • 使用 aki::Value 作为键或元素时,需要配合 aki::Value::HashPair 哈希函数使用

示例:

  • C++

    #include <aki/jsbind.h>
    #include <unordered_map>
    #include <unordered_set>
    
    std::map<std::string, int> FooMap() {
        std::map<std::string, int> map = {{"key1", 1}, {"key2", 2}};
        return map;
    }
    
    std::unordered_map<std::string, int> FooHashMap() {
        std::unordered_map<std::string, int> hashMap = {{"key1", 1}, {"key2", 2}};
        return hashMap;
    }
    
    std::unordered_set<int> FooHashSet() {
        std::unordered_set<int> hashSet = {1, 2, 3};
        return hashSet;
    }
    
    JSBIND_GLOBAL() {
        JSBIND_FUNCTION(FooMap);
        JSBIND_FUNCTION(FooHashMap);
        JSBIND_FUNCTION(FooHashSet);
    }
    JSBIND_ADDON(hello)
    
  • ETS

    import libAddon from 'libhello.so'
    let map = libAddon.FooMap();
    let hashMap = libAddon.FooHashMap();
    let hashSet = libAddon.FooHashSet();
    
  • Example

Optional

AKI 支持使用 C++ 的 std::optional<T> 类型,用于表示可能不存在的值。

  • ETS 侧 undefined 会被转换为 std::nullopt
  • C++ 侧 std::nullopt 会被转换为 ETS 的 undefined

示例:

  • C++

    #include <aki/jsbind.h>
    #include <optional>
    
    std::optional<int> FooOptional(bool hasValue) {
        if (hasValue) {
            return 42;
        }
        return std::nullopt;
    }
    
    JSBIND_GLOBAL() {
        JSBIND_FUNCTION(FooOptional);
    }
    JSBIND_ADDON(hello)
    
  • ETS

    import libAddon from 'libhello.so'
    let value1 = libAddon.FooOptional(true);  // 返回 42
    let value2 = libAddon.FooOptional(false); // 返回 undefined
    
  • Example

ArrayBuffer

二进制数据缓冲区ArrayBuffer, TypedArray 是 ETS AKI 提供了内建结构体:aki::ArrayBuffer用来支持该特性:

  • GetData()* 获取 ArrayBuffer 数组缓冲区地址,aki::ArrayBuffer 本身不申请数据内存,data 都来源于ETS引擎分配的内存,也无需做内存生命周期管理,禁止对该内存进行危险的释放

  • GetLength() 获取 ArrayBuffer 数组缓冲区长度,以单字节为计量单位。

  • GetTyped() 获取 ArrayBuffer 数组缓冲区的类型化类型。

  • GetCount() 获取 ArrayBuffer 数组缓冲区的类型化数据元素个数。

示例:

  • C++
#include <aki/jsbind.h>
aki::ArrayBuffer PassingArrayBufferReturnArrayBuffer(aki::ArrayBuffer origin) {
    aki::ArrayBuffer buff(origin.GetData(), origin.GetCount());
    uint8_t* data = buff.GetData();
    data[4] = 4;
    data[5] = 5;
    data[6] = 6;
    data[7] = 7;

    return buff;
}
  • ETS
import libAddon from 'libarraybuffer2native.so'

let buff: ArrayBuffer = new ArrayBuffer(8);
let uint8Buff1: Uint8Array = new Uint8Array(buff);
uint8Buff1[0] = 0;
uint8Buff1[1] = 1;
uint8Buff1[2] = 2;
uint8Buff1[3] = 3;
let result: ArrayBuffer = libAddon.PassingArrayBufferReturnArrayBuffer(buff);
uint8Buff1 = new Uint8Array(result);
let message: String = uint8Buff1.toString();

JsonObject

ETS支持使用JsonObject表示key-value结构的数据类型,如:

{
  name: 'hanmeimei',
  age: '17',
  date: '1999-02-02'
}

AKI支持使用C/C++的std::map<std::string, T>映射ETS的JsonObject

  • std::map<std::string, T>对应的JsonObject必须约束value类型一致

  • Example

  • C++

void Foo(std::map<std::string, int> obj)
{
    for (auto& iter : obj) {
        ......; // key: iter.first; value: iter.second
    }
}

JSBIND_GLOBAL() {
    JSBIND_FUNCTION(Foo);
}
  • ETS
import libmap_for_object from 'libmap_for_object.so'

let a = {age: 100};
libmap_for_object.Foo(a);

Function

Function是JS的一种基本数据类型,当JS传入Function作为参数时,Native可在适当的时机调用触发回调。AKI 支持如下3中C++数据类型作为参数处理回调:

  • aki::Callback<R (P...)>:指定回调类型为R (*)(P...)高性能回调。非线程安全,禁止在非JS线程使用,否则会发生异常;
  • aki::SafetyCallback<R (P...)>:指定回调类型为R (*)(P...)的线程安全回调。因为需要创建线程安全资源,所以性能不如aki::Callback;
  • std::function<R (P...)>:用法与aki::SafetyCallback一致;

对象引用与指针

C++ 对象作为参数和返回类型,在 C++ & ETS 代码中可以使用如下形式进行传递:

  • 值传递;
  • 引用(T&)与指针(T*)传递;
  • Example

ETS与C/C++建立联系步骤

  1. 快速接入AKI后正确包含头文件
#include <aki/jsbind.h>

注:使用aki只有一种方式,包含头文件aki/jsbind.h;不能在没有包含aki/jsbind.h的情况下只包含其他头文件,比如

#include "aki/definer/function_definer.h"
#include "aki/definer/class_definer.h"
#include "aki/value/value.h"
...

如果错误包含头文件可能会出现以下错误

1.error: no member named xxx in aki::Binder 异常
2.error: no template named xxx
3.error: use of undeclared xxx
...
  1. 注册 AKI 插件、FFI 特性
#include <aki/jsbind.h>

// Step 1 注册 AKI 插件
JSBIND_ADDON(hello) // 注册 AKI 插件名: 即为编译*.so名称,规则与NAPI一致

// Step 2 注册 FFI 特性、使用 JSBind 工具宏声明需要被绑定的类、函数
JSBIND_GLOBAL()
{
  JSBIND_FUNCTION(funcName);
}

JSBIND_CLASS(className) {
    JSBIND_CONSTRUCTOR<>();
    JSBIND_METHOD(xxx);
    JSBIND_PROPERTY(xxx);
    JSBIND_PROPERTY(xxx);
    ···
}
  1. 导出绑定的类、函数等

接口说明

JSBind语法糖

语法糖 AKI 说明
插件注册 ETS 访问 C++ JSBIND_ADDON 注册OpenHarmony Native 插件。
使用指导
C++ 访问 ETS aki::Value::FromGlobal 获取 ETS 侧globalThis下的属性。
使用指导
全局函数 ETS 访问 C++ JSBIND_FUNCTION
JSBIND_PFUNCTION
绑定 C++ 全局方法,ETS 可调用。
使用指导
C++ 访问 ETS aki::Value::operator() ETS 全局方法函数调用运算符,C++ 可调用。
使用指导
类构造函数 ETS 访问 C++ JSBIND_CONSTRUCTOR<> 绑定 C++ 类构造函数,ETS 可调用。构造函数可重载,需指定构造函数参数类型。
使用指导
C++ 访问 ETS - 暂不支持
类成员函数 ETS 访问 C++ JSBIND_METHOD
JSBIND_PMETHOD
绑定 C++ 类成员函数,ETS 可调用。
成员函数可以为:类静态函数,类成员函数,const类成员函数。
使用指导
C++ 访问 ETS aki::Value::CallMethod 调用 ETS 对象的成员函数。
使用指导
类成员属性 ETS 访问 C++ JSBIND_PROPERTY
JSBIND_FIELD
JSBIND_PROPERTY 绑定 C++类成员属性;
JSBIND_FIELD 绑定 C++ 类成员属性访问器(Get/Set)。
使用指导
C++ 访问 ETS aki::Value::operator[] JS对象的属性访问运算符
使用指导
枚举类型 ETS 访问 C++ JSBIND_ENUM
JSBIND_ENUM_VALUE
C/C++侧默认枚举类型为POD中的int32_t,ETS侧对应的枚举类型属性为readonly。
使用指导
C++ 访问 ETS - -

插件注册

JSBIND_ADDON(addonName)

使用JSBIND_ADDON注册OpenHarmony Native 插件,可从 ETS import 导入插件。

参数:

参数名 类型 必填 说明
addonName - Y 注册的OpenHarmony native 插件名,可从 ETS import lib${addonName}.so 导入插件,插件名必须符合函数命名规则。

示例:

  • C++
#include <string>
#include <aki/jsbind.h>

JSBIND_ADDON(addon0)

  • ETS
import addon from 'libaddon0.so' // 插件名为:addon0

JSBIND_ADDON_X(addonName constructorAlias)

用法与JSBIND_ADDON相似,用于支持插件名有特殊符号的场景,如包含'-';

参数:

参数名 类型 必填 说明
addonName - Y 注册的OpenHarmony native 插件名,可从 ETS import lib${addonName}.so 导入插件,插件名可包含特殊符号,如:'-'。
constructorAlias - Y 插件预构造函数名,只需填写符合函数命名规则名称即可,无其它特殊含义

示例:

  • C++
#include <string>
#include <aki/jsbind.h>

JSBIND_ADDONX(hello-world, HelloWorld)

  • ETS
import addon from 'libhello-world.so' // 插件名为:hello-world

绑定全局函数

JSBIND_GLOBAL

用于圈定需要绑定的全局函数 scope。

JSBIND_FUNCTION(func, alias)

JSBIND_GLOBAL作用域下使用JSBIND_FUNCTION绑定 C++ 全局函数后,可从 ETS 直接调用。

  • 调度线程为 ETS 线程;

参数:

参数名 类型 必填 说明
func 函数指针 Y 被绑定的C++函数指针,当alias未被指定时,ETSC++函数名相同。
alias string N 函数别名

示例:

  • C++
#include <string>
#include <aki/jsbind.h>

std::string SayHello(std::string msg)
{
    return msg + " too.";
}

JSBIND_GLOBAL()
{
    JSBIND_FUNCTION(SayHello);
}

JSBIND_ADDON(hello);
  • ETS
import aki from 'libhello.so' // 插件名

let message = aki.SayHello("hello world");

JSBIND_PFUNCTION(func, alias)

使用JSBIND_PFUNCTION绑定 C++ 全局函数后,从 ETS 使用同Promise方式相同的异步调用。

  • 调度线程为工作线程,由 ArkCompiler Runtime 决定;

参数:

参数名 类型 必填 说明
func 函数指针 Y 被绑定的C++函数指针。
alias string N 函数别名

示例:

  • C++

int AsyncTaskReturnInt() {
    // Do something;
    return -1;
}

JSBIND_GLOBAL() {
    JSBIND_PFUNCTION(AsyncTaskReturnInt);
}

JSBIND_ADDON(async_tasks);
  • ETS
import libAddon from 'libasync_tasks.so'

libAddon.AsyncTaskReturnInt().then(res => {
  console.log('[AKI] AsyncTaskReturnInt: ' + res)
});

绑定类/结构体

AKI 提供 JSBIND_CLASS 对 C++ 类/结构体进行绑定,在JSBIND_CLASS作用域下可绑定:类构造函数、类成员函数、类成员属性的类特性。

JSBIND_CLASS(class)

参数:

参数名 类型 必填 说明
class class/struct Y 被绑定的C++类对象/结构体ETSC++类名相同。

类构造函数

JSBIND_CLASS作用域下使用绑定 C++ 类/结构体构造函数,其中为了支持多态,可通过类型模板指定构造函数参数类型。

  • JSBIND_CONSTRUCTOR 需要在JSBIND_CLASS的作用域下;

参数:

参数名 类型 必填 说明
T any N 构造函数参数类型,可变类型参数。

示例:

  • C++
#include <string>
#include <aki/jsbind.h>

class TestObject {
public:
    TestObject();
    
    explicit TestObject(double) {
        // ...
    }
    
    ~TestObject() = default;
} // TestObject

JSBIND_CLASS(TestObject)
{
    JSBIND_CONSTRUCTOR<>();
    JSBIND_CONSTRUCTOR<double>();
}
JSBIND_ADDON(hello);
  • ETS
import aki from 'libhello.so' // 插件名

var obj1 = new aki.TestObject();
var obj2 = new aki.TestObject(3.14);

绑定类成员函数

JSBIND_METHOD(method)

AKI 使用 JSBIND_METHOD 对C++ 的3种类成员函数进行绑定:类静态函数、类成员函数、const 类成员函数。

  • JSBIND_METHOD 需要在JSBIND_CLASS的作用域下;
  • 调度线程为 JS 线程;

参数:

参数名 类型 必填 说明
method R (C:😗)(P...) Y 同时支持类静态函数、类成员函数、const 类成员函数。

示例:

使用 AKI 对C++类成员函数绑定

#include <string>
#include <aki/jsbind.h>

class TestObject {
public:
    TestObject();
    
    explicit TestObject(double) {
        // ...
    }
    
    ~TestObject() = default;
    
    static double MultiplyObject(TestObject obj1, TestObject obj2) {
        return obj1.value_ * obj2.value_;
    }
    
    double Multiply(double mult) {
        value_ *= mult;
        return value_;
    }

private:
    double value_;
} // TestObject

JSBIND_CLASS(TestObject)
{
    JSBIND_CONSTRUCTOR<>();
    JSBIND_CONSTRUCTOR<double>();
    JSBIND_METHOD(MultiplyObject);
    JSBIND_METHOD(Multiply);
}
JSBIND_ADDON(hello);

例:ETS 侧调用绑定的C++类成员函数

import aki from 'libhello.so' // 插件名

var obj1 = new aki.TestObject();
var obj2 = new aki.TestObject(3.14);
obj1.Multiply(-1);
aki.TestObject.MultiplyObject(obj1, obj2) // 静态方法

JSBIND_PMETHOD(method)

JSBIND_PMETHOD用于绑定 C++ 类成员函数,从 ETS 使用同Promise方式相同的异步调用。

  • 调度线程为工作线程,由 ArkCompiler Runtime 决定;

参数:

参数名 类型 必填 说明
method 类成员函数指针 Y 被绑定的C++类成员函数指针。

示例:

  • C++

class TaskRunner {
public:
    TaskRunner() = default;
    std::string DoTask() {
      // Do something;
      return "done.";
    }
};

JSBIND_CLASS(TaskRunner) {
    JSBIND_CONSTRUCTOR<>();
    JSBIND_PMETHOD(DoTask);
}

int AsyncTaskReturnInt() {
    // Do something;
    return -1;
}

JSBIND_GLOBAL() {
    JSBIND_PFUNCTION(AsyncTaskReturnInt);
}

JSBIND_ADDON(async_tasks);
  • ETS
import libAddon from 'libasync_tasks.so'

let taskRunner = new libAddon.TaskRunner();
taskRunner.DoTask().then(res => {
  console.log('[AKI] DoTask: ' + res)
});

libAddon.AsyncTaskReturnInt().then(res => {
  console.log('[AKI] AsyncTaskReturnInt: ' + res)
});

绑定类成员属性

JSBIND_PROPERTY(property) new in 1.0.7

AKI 使用JSBIND_PROPERTYJSBIND_FIELD 对 C++ 的类成员属性、类成员属性访问器进行绑定

  • JSBIND_PROPERTY需要在JSBIND_CLASS的作用域下;

参数:

参数名 类型 必填 说明
property T Y 类成员属性名。

示例:

  • C++
#include <string>
#include <aki/jsbind.h>

class TestObject {
public:    
    explicit TestObject(double) {
        // ...
    }
    
    ~TestObject() = default;

    double value_;
} // TestObject

JSBIND_CLASS(TestObject)
{
    JSBIND_CONSTRUCTOR<double>();
    JSBIND_PROPERTY(value_);
}
  • ETS
import aki from 'libhello.so' // 插件名

var obj = new aki.TestObject(3.14);
obj.value = 1;
let value = obj.value;

JSBIND_FIELD(field, getter, setter)

AKI 使用JSBIND_FIELD 对 C++ 的类成员属性进行监听

  • JSBIND_FIELD 需要在JSBIND_CLASS的作用域下;

  • 调度线程为 ETS 线程;

参数:

参数名 类型 必填 说明
field T Y 类成员属性名。
getter T (void) Y get属性访问器。
setter void (T) Y set属性访问器。

示例:

  • C++
#include <string>
#include <aki/jsbind.h>

class TestObject {
public:    
    explicit TestObject(double) {
        // ...
    }
    
    ~TestObject() = default;
    
    double GetValue() const {
        return value_;
    }

    void SetValue(double value) {
        value_ = value;
    }

private:
    double value_;
} // TestObject

JSBIND_CLASS(TestObject)
{
    JSBIND_CONSTRUCTOR<double>();
    JSBIND_FIELD("value", GetValue, SetValue);
}
  • ETS
import aki from 'libhello.so' // 插件名

var obj = new aki.TestObject(3.14);
obj.value = 1;
let value = obj.value;

类继承

AKI 支持绑定 C++ 类继承关系,子类可以继承父类的成员属性和方法。

JSBIND_EXTEND(parentClass)

JSBIND_CLASS作用域下使用JSBIND_EXTEND指定父类,子类可以访问父类的公开成员。

参数:

参数名 类型 必填 说明
parentClass class/struct Y 父类名。

示例:

  • C++
#include <string>
#include <aki/jsbind.h>

class Person {
public:
    Person(std::string name, int age) : name_(name), age_(age) {}
    ~Person() = default;

    virtual void SetName(std::string name) { name_ = name; }
    virtual void SetAge(int age) { age_ = age; }
    virtual std::string GetName() const { return name_; }
    virtual int GetAge() const { return age_; }

private:
    std::string name_;
    int age_;
};

class Student : public Person {
public:
    Student(std::string name, int age) : Person(name, age) {}
    ~Student() = default;

    void SetGrade(int grade) { grade_ = grade; }
    int GetGrade() const { return grade_; }    
    void SetAge(int age) override { Person::SetAge(age); }
    int GetAge() const override { return Person::GetAge(); }

private:
    int grade_;
};

JSBIND_CLASS(Person)
{
    JSBIND_CONSTRUCTOR<std::string, int>();
    JSBIND_METHOD(SetName);
    JSBIND_METHOD(SetAge);
    JSBIND_METHOD(GetName);
    JSBIND_METHOD(GetAge);
}

JSBIND_CLASS(Student)
{
    JSBIND_EXTEND(Person); // 导入父类
    
    JSBIND_CONSTRUCTOR<std::string, int>();
    JSBIND_METHOD(SetGrade);
    JSBIND_METHOD(GetGrade);
    JSBIND_METHOD(SetAge);
    JSBIND_METHOD(GetAge);
}

JSBIND_ADDON(hello);
  • ETS
import aki from 'libhello.so'

let stu = new aki.Student("Tom", 18);
stu.SetGrade(90);
stu.SetAge(19);
stu.GetName(); // 调用继承的方法
stu.GetGrade();

绑定枚举类型

JSBind语法糖JSBIND_ENUMJSBIND_ENUM_VALUE支持绑定 C/C++ 枚举类型,映射为 ETS 的Number类型。

  • C/C++侧默认枚举类型为POD中的int32_t;
  • ETS侧对应的枚举类型属性为readonly

JSBIND_ENUM(enum)

参数:

参数名 类型 必填 说明
enum enum Y 被绑定的C++枚举类型。

JSBIND_ENUM_VALUE(value)

参数:

参数名 类型 必填 说明
value enum::value Y 被绑定的C++枚举值。

示例:

  • C++
#include <string>
#include <aki/jsbind.h>

enum TypeFlags {
    NONE,
    NUM,
    STRING,
    BUTT = -1
};

JSBIND_ENUM(TypeFlags) {
    JSBIND_ENUM_VALUE(NONE);
    JSBIND_ENUM_VALUE(NUM);
    JSBIND_ENUM_VALUE(STRING);
}

TypeFlags Passing(TypeFlags flag) {
    return flag;
}

JSBIND_GLOBAL()
{
    JSBIND_FUNCTION(Passing);
}

JSBIND_ADDON(enumeration);
  • ETS
import libAddon from 'libenumeration.so' // 插件名


console.log('AKI libAddon.TypeFlags.NONE = ' + libAddon.TypeFlags.NONE);
console.log('AKI libAddon.TypeFlags.NUM = ' + libAddon.TypeFlags.NUM);
console.log('AKI libAddon.TypeFlags.Passing() = ' + libAddon.Foo(libAddon.TypeFlags.STRING));
try {
  libAddon.TypeFlags.NUM = 10; // TypeError: Cannot set readonly property
} catch (error) {
  console.error('AKI catch: ' + error);
}

线程安全函数

使用AKI的线程安全特性,绑定 ETS 的业务函数后,可由native直接调用。

  • 线程安全:使用AKI线程安全绑定的 ETS 函数是线程安全的,可在非 ETS 线程直接调用。最终会由框架调度 ETS 线程执行业务;
  • 阻塞式调用:C++ 触发调用 ETS 函数的调用是阻塞式的,对于在 ETS 线程执行业务这点没有疑义。但当C++触发 ETS 业务调用的线程是非JS线程时,就存在跨线程任务调度。此时由框架进行了阻塞式调用,即 C++ 会等待 ETS 函数执行结束后返回;

JSBind.bindFunction(name: string, func: function)

在 ETS 使用 JSBind.bindFunction 绑定 ETS 全局函数后,可从 C++ 直接调用。

参数:

参数名 类型 必填 说明
name string Y 指定绑定的ETS函数名,用于Native索引(索引标识需保证全局唯一)。
func function Y 被绑定的ETS函数

返回值:

类型 说明
number 当前被绑定的函数下标索引
// name: 指定函数名,func: ETS 全局函数
libAddon.JSBind.bindFunction(name: string, func: Function);

C++ 使用aki::JSBind::GetJSFunction获取指定 ETS 函数句柄后,使用Invoke触发调用

auto jsFunc = aki::JSBind::GetJSFunction("xxx"); // 获取指定函数句柄
auto result = jsFunc->Invoke<T>(...); // 调用ETS函数,Invoke<T>指定返回值类型
  • ETS
import libAddon from 'libhello.so' // 插件名

function sayHelloFromJS (value) {
  console.log('what do you say: ' + value);
  return "hello from JS"
}

libAddon.JSBind.bindFunction("sayHelloFromJS", sayHelloFromJS);
  • C++
#include <string>
#include <aki/jsbind.h>

void DoSomething() {
    // 索引 ETS 函数句柄
    auto jsFunc = aki::JSBind::GetJSFunction("sayHelloFromJS");

    // Invoke 指定 ETS 方法的返回值类型
    auto result = jsFunc->Invoke<std::string>("hello from C++"); // 可在非ETS线程执行
    // result == "hello from JS"
}

查看示例:bind_from_js

JSBind.unbindFunction(name: string)

解绑之前绑定的 ETS 全局函数。

参数:

参数名 类型 必填 说明
name string Y 指定需要解绑的ETS函数名。

返回值:

类型 说明
int 0表示成功,-1表示失败。
// 解绑指定函数
aki::JSBind::unbindFunction("sayHelloFromJS");

napi_env获取

static napi_env aki::JSBind::GetScopedEnv();

线程安全函数,用于获取当前线程的 napi_env 对象。当在非 ETS 线程调用时,返回 nullptr。

参数:

参数名 类型 必填 说明
key string Y 需要读取的属性名。

示例:

// 在 ETS 线程执行
napi_value obj;
napi_env env = aki::JSBind::GetScopedEnv();
napi_create_object(env, &obj);

TaskRunner

TaskRunner提供 ETS 线程的任务调度器,开发人员可以很方便地往 ETS 线程PostTask

JSBind.initTaskRunner(name: string)

ETS侧的静态函数,用于初始化对应 ETS 线程的任务调度器。

参数:

参数名 类型 必填 说明
runnerName string Y 任务调度器别名。

示例:

import libAddon from "libaki.so"

libAddon.JSBind.initTaskRunner("name");

PostTask

static void PostTask(const std::string& runnerName, Closure task);

静态函数,往指定任务调度器,投递任务。

参数:

参数名 类型 必填 说明
runnerName string Y 指定任务调度器,需先使用JSBind.initTaskRunner初始化任务调度器。
task Closure Y 任务表达式: std::function<void ()>。

示例:


void foo ()
{
    aki::TaskRunner::PostTask("main", [] () {
      // 在 ETS 线程执行
      // do something
    });
}

特别声明

PostTask是将任务task抛回到 ETS 线程中运行。在 ETS 线程运行期间构造的 ETS 对象是不会被系统GC的,需要使用者自己管理这些 ETS 对象的生命周期。如果使用者不对使用对象的生命周期进行管理,容易造成内存泄露问题。
一般使用napi_handle_scaope来对使用对象的生命周期进行管理:

void foo ()
{
    aki::TaskRunner::PostTask("main", [] () {
      // 在 JS 线程执行
      napi_handle_scope scope;
      napi_env env = Binding::GetScopedEnv();
      napi_open_handle_scope(env, &scope);
      // do something
      napi_close_handle_scope(env, scope);
    });
}

AsyncWorker

AsyncWorker提供异步工作队列的能力,支持在工作线程中执行耗时操作,并通过Promise返回结果。

constructor

AsyncWorker()
AsyncWorker(napi_async_execute_callback execute, napi_async_complete_callback complete, void *data)

创建AsyncWorker对象,可以指定执行回调、完成回调和用户数据。 (下同SetData、SetExecuteCallback、SetCompleteCallback的入参)。

SetData

int SetData(void *data)

设置用户数据,该数据为执行回调和完成回调的共享数据。

参数:

参数名 类型 必填 说明
data void* Y 用户数据指针。

返回值:

类型 说明
int 0表示成功,-1表示失败。

SetExecuteCallback

int SetExecuteCallback(napi_async_execute_callback execute)

设置异步执行回调函数,该函数在工作线程中执行。

参数:

参数名 类型 必填 说明
execute napi_async_execute_callback Y 执行回调函数。

返回值:

类型 说明
int 0表示成功,-1表示失败。

SetCompleteCallback

int SetCompleteCallback(napi_async_complete_callback complete)

设置异步完成回调函数,该函数在 ETS 线程中执行。

参数:

参数名 类型 必填 说明
complete napi_async_complete_callback Y 完成回调函数。

返回值:

类型 说明
int 0表示成功,-1表示失败。

CreateAsyncWorker

napi_value CreateAsyncWorker(const char *resource_name = "default")

创建Promise对象和异步工作。

参数:

参数名 类型 必填 说明
resource_name const char* N 资源名称,不传参数则默认为"default"。

返回值:

类型 说明
napi_value Promise对象的napi_value句柄。

Queue

bool Queue()

将异步工作加入系统队列执行。

返回值:

类型 说明
bool true表示成功,false表示失败。

示例:

#include "aki/jsbind.h"
#include "aki/asyncworker/asyncworker.h"

void ExecuteCallback(napi_env env, void* data) {
  // 在工作线程中执行耗时操作
    std::this_thread::sleep_for(std::chrono::seconds(3));
}

void CompleteCallback(napi_env env, napi_status status, void* data) {
    auto asyncData = static_cast<aki::AsyncWorker::AsyncData* >(data);
    napi_deferred deferred = asyncData->deferred_;
    napi_value result;
    napi_create_string_utf8(env, "success", NAPI_AUTO_LENGTH, &result);
    napi_resolve_deferred(env, deferred, result);
}

napi_value CreateAsyncWorker() {
    aki::AsyncWorker worker;
    worker.SetExecuteCallback(ExecuteCallback);
    worker.SetCompleteCallback(CompleteCallback);
    napi_value promise = worker.CreateAsyncWorker();
    worker.Queue();
    return promise;
}

aki-value

ETS 是弱类型语言,可用泛型any表示任意类型。C/C++使用aki::Value映射 ETS 的any类型

功能概览表

方法 签名 说明
静态工厂方法
FromGlobal static Value FromGlobal(const char* key) 获取 ETS 侧 globalThis 下的属性
NewObject static Value NewObject() 创建 ETS 对象
NewArray static Value NewArray() 创建 ETS 数组对象
类型判断方法
IsUndefined bool IsUndefined() const 判断是否为 undefined
IsNull bool IsNull() const 判断是否为 null
IsBool bool IsBool() const 判断是否为 boolean
IsNumber bool IsNumber() const 判断是否为 number
IsString bool IsString() const 判断是否为 string
IsArray bool IsArray() const 判断是否为数组
IsFunction bool IsFunction() const 判断是否为 function
IsObject bool IsObject() const 判断是否为对象(排除数组、空值)
类型转换方法
As<T> template<typename T> T As() const 将 ETS 对象转换为 C/C++ 指定类型
属性/元素访问
operator[] Value operator[](const std::string& key) const 访问对象属性
operator[] Value operator[](const size_t index) const 访问数组元素
Set template<typename V> void Set(const char* key, const V& value) 设置属性值
函数调用
operator() template<typename... Args> Value operator()(Args&&... args) const 函数调用运算符
CallMethod template<typename... Args> Value CallMethod(const char* name, Args&&... args) 调用成员函数
句柄获取
GetHandle napi_value GetHandle() const 获取 napi_value 句柄

aki::Value::FromGlobal

static Value FromGlobal(const char* key = nullptr)

用于获取 ETS 侧globalThis下的属性。

参数:

参数名 类型 必填 说明
key string Y 需要读取的属性名。

返回值:

类型 说明
aki::Value 对应属性的 ETS 对象句柄。

示例:

  // 获取globalThis.JSON
  aki::Value json = aki::Value::FromGlobal("JSON");
  json["stringify"](obj);

aki::Value::NewObject

static Value NewObject();

创建aki::Value泛型对象。

返回值:

类型 说明
aki::Value 返回泛型对象。

示例:

  aki::Value val = aki::Value::NewObject();
  val.Set("name", "aki"); // {'name': 'aki'};

aki::Value::NewArray

static Value NewArray();

创建 ETS 数组对象。

返回值:

类型 说明
aki::Value 返回数组对象。

示例:

  aki::Value arr = aki::Value::NewArray();
  arr.Set(0, std::string("first"));
  arr.Set(1, std::string("second"));

aki::Value::IsUndefined

bool IsUndefined() const

判断 ETS 对象类型是否为undefined

返回值:

类型 说明
bool true or false。

aki::Value::IsNull

bool IsNull() const

判断 ETS 对象类型是否为null

返回值:

类型 说明
bool true or false。

aki::Value::IsBool

bool IsBool() const

判断 ETS 对象类型是否为boolean

返回值:

类型 说明
bool true or false。

aki::Value::IsNumber

bool IsNumber() const

判断 ETS 对象类型是否为number

返回值:

类型 说明
bool true or false。

aki::Value::IsString

bool IsString() const

判断 ETS 对象类型是否为string

返回值:

类型 说明
bool true or false。

aki::Value::IsArray

bool IsArray() const

判断 ETS 对象类型是否为数组[]

返回值:

类型 说明
bool true or false。

aki::Value::IsFunction

bool IsFunction() const

判断 ETS 对象类型是否为function

返回值:

类型 说明
bool true or false。

aki::Value::IsObject

bool IsObject() const

判断 ETS 值是否为对象类型(排除数组、空值)。

返回值:

类型 说明
bool true or false。

aki::Value::As

template<typename T>
T As() const;

模板函数,用于将 ETS 对象转化为 C/C++ 指定数据类型

参数:

参数名 类型 必填 说明
T any Y 需要被转化的 C/C++ 数据类型。

返回值:

类型 说明
T 对应类型的值。

示例:

  value.As<bool>(); // 将 ETS 对象 value 转化为 bool
  value.As<int>(); // 将 ETS 对象 value 转化为 int
  value.As<std::string>(); // 将 ETS 对象 value 转化为 string

aki::Value::operator[]

Value operator[](const std::string& key) const;
Value operator[](const size_t index) const;

aki::Value对象的下标运算符

参数:

参数名 类型 必填 说明
key string Y 属性名下标。
index size_t Y 数组下标。

返回值:

类型 说明
aki::Value 返回泛型对象。

示例:

  // value 映射为 ETS 数组对象 let value = ['aki', 'jsbind'];
  // 访问下标为0的值:'aki';
  aki::Value str = value[0]; // str = "aki"

  // 调用 JSON.stringify(...);
  aki::Value::FromGlobal("JSON")["stringify"](...);

aki::Value::Set

template<typename V>
void Set(const char* key, const V& value);

用于给aki::Value泛型对象属性设值。

参数:

参数名 类型 必填 说明
key string Y 属性名。
value any Y 属性值。

示例:

  // value 为 JS 对象;
    value.Set("name", "aki");

aki::Value::operator()

template<typename... Args>
Value operator()(Args&&... args) const;

aki::Value对象的函数调用运算符

参数:

参数名 类型 必填 说明
args any N 函数所接收入参。

返回值:

类型 说明
aki::Value 返回泛型对象。

示例:

  // 调用 JSON.parse({'aki': 'jsinbd'});
  aki::Value::FromGlobal("JSON")["parse"]({"aki": "jsinbd"});

aki::Value::CallMethod

template<typename... Args>
Value CallMethod(const char* name, Args&&... args)

调用 ETS 对象的成员函数。

参数:

参数名 类型 必填 说明
name string Y 函数名。
args any N 成员函数接收的参数。

返回值:

类型 说明
aki::Value 返回泛型对象。

示例:

  // value 映射为 ETS 数组对象 let value = ['aki'];
  // 调用 value.push('jsbind');
  value.CallMethod("push", "jsbind");

aki::Value::GetHandle

napi_value GetHandle() const

用于获取 ETS 对象的 napi_value 句柄。

返回值:

类型 说明
napi_value ETS 对象的 napi_value 句柄。

C/C++ 调用 @ohos.bundle.bundleManager (bundleManager模块)特性

示例:

  • 期望在 C++ 调用如下@ohos.bundle.bundleManager (bundleManager模块) 特性:

    如下ArkTS代码为@ohos.bundle.bundleManager 示例

    import bundleManager from '@ohos.bundle.bundleManager';
    import hilog from '@ohos.hilog';
    let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT;
    try {
        bundleManager.getBundleInfoForSelf(bundleFlags).then((data) => {
            hilog.info(0x0000, 'testTag', 'getBundleInfoForSelf successfully. Data: %{public}s', JSON.stringify(data));
        }).catch(err => {
            hilog.error(0x0000, 'testTag', 'getBundleInfoForSelf failed. Cause: %{public}s', err.message);
        });
    } catch (err) {
        hilog.error(0x0000, 'testTag', 'getBundleInfoForSelf failed: %{public}s', err.message);
    }
    

    使用如下C++代码实现上述功能

      /* 要求在ArkTS侧执行如下代码:
      * import bundleManager from '@ohos.bundle.bundleManager';
      * globalThis.bundleManager = bundleManager;
      */
      aki::Value bundleManager = aki::Value::FromGlobal("bundleManager");
      
      /* 如下 C++ 代码等同于 ETS 代码:
      * let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT;
      * bundleManager.getBundleInfoForSelf(bundleFlags).then((data) => {
      *   console.log('getBundleInfoForSelf successfully. Data:', JSON.stringify(data));
      * })
      */
      std::function<void(aki::Value)> thenFunc = [](aki::Value data) {
          AKI_LOG(INFO) << aki::Value::FromGlobal("JSON")["stringify"](data).As<std::string>();
      };
      int bundleFlags = bundleManager["BundleFlag"]["GET_BUNDLE_INFO_DEFAULT"].As<int>();
      bundleManager["getBundleInfoForSelf"](bundleFlags).CallMethod("then", thenFunc);
    
  • Example

aki-promise

aki::Promise 提供了在 C++ 侧创建和管理 Promise 对象的能力,支持异步操作的返回值处理。

constructor

Promise()
Promise(napi_value promise)

创建 Promise 对象,可从现有的 napi_value 构造。

Resolve

template<typename T>
void Resolve(T&& t) const

解决 Promise,返回成功结果。

参数:

参数名 类型 必填 说明
t T Y 要返回的值,支持任意 AKI 支持的类型。

Reject

template<typename T>
void Reject(T&& t) const

拒绝 Promise,返回错误信息。

参数:

参数名 类型 必填 说明
t T Y 错误信息,支持任意 AKI 支持的类型。

GetHandle

napi_value GetHandle()

获取 Promise 的 napi_value 句柄。

返回值:

类型 说明
napi_value Promise 的 napi_value 句柄。

示例:

#include <aki/jsbind.h>
#include <aki/promise.h>

aki::Promise CreatePromise() {
    aki::Promise promise;
    promise.Resolve("success");
    return promise;
}

aki-ArrayBuffer

constructor

  • 当在非 ETS 线程使用 aki::ArrayBuffer,需要关注数据字节流生命周期,并考虑是否需要结合Commit()函数使用。
ArrayBuffer(uint8_t* ptr, size_t len, Typed typed = BUFF)

参数:

参数名 类型 必填 说明
ptr uint8_t* Y 构造 ArrayBuffer 的数据字节流内存地址。
len size_t Y 构造 ArrayBuffer 的数据字节流内存长度。
typed aki::ArrayBuffer::Typed N 构造的 ArrayBuffer | TypedArray 类型,默认为 ArrayBuffer。

示例:

uint8_t temp[4] = {10, 20, 30, 40};
aki::ArrayBuffer arrayBuffer(temp, 4);

GetData

uint8_t* GetData()

获取 ArrayBuffer 的数据字节流内存地址。

返回值:

类型 说明
uint8_t* ArrayBuffer 的数据字节流内存地址。

GetLength

size_t GetLength()

获取 ArrayBuffer 的数据字节流内存长度。

返回值:

类型 说明
size_t ArrayBuffer 的数据字节流内存长度。

Commit

void Commit()

当在非 ETS 线程使用 ArrayBuffer 时,如果数据字节流的内存生命周期在 ArrayBuffer 使用前结束,则需要暂存。

返回值:

类型 说明
size_t ArrayBuffer 的数据字节流内存长度。

示例:

// 非 ETS 线程
aki::ArrayBuffer AsyncTaskReturnArrayBufferWithCommit() {
    uint8_t temp[4] = {10, 20, 30, 40};
    aki::ArrayBuffer arrayBuffer(temp, 4);
    arrayBuffer.Commit();
    return arrayBuffer;
}

aki::Binding

Binding 类是 AKI 框架的核心注册管理类,负责管理所有需要绑定到 ETS 的函数、类、枚举等对象的注册和查找。这是一个纯静态类。

Binding::SetScopedEnv

static void SetScopedEnv(napi_env env)

设置当前线程的 NAPI 环境句柄。

参数:

参数名 类型 必填 说明
env napi_env Y NAPI 环境句柄。

Binding::GetScopedEnv

static napi_env GetScopedEnv()

获取当前线程的 NAPI 环境句柄。

返回值:

类型 说明
napi_env 当前线程的 NAPI 环境句柄,非 ETS 线程返回 nullptr。

Binding::GetFunctionList

static std::forward_list<Function>& GetFunctionList()
static std::forward_list<Function>& GetFunctionList(const char* module)

获取全局函数列表。

参数:

参数名 类型 必填 说明
module const char* N 模块名,不传则获取全局函数列表。

返回值:

类型 说明
std::forward_list& 函数列表引用。

Binding::RegisterFunction

static void RegisterFunction(const char *name, int32_t invokerId, Binder* binder)
static void RegisterFunction(const char* module, const char *name, int32_t invokerId, Binder* binder)

注册全局函数。

参数:

参数名 类型 必填 说明
module const char* N 模块名,不传则注册在全局函数列表
name const char* Y 函数名。
invokerId int32_t Y 调用器ID。
binder Binder* Y 绑定器指针。

Binding::GetClassList

static std::forward_list<ClassBase*>& GetClassList()

获取已注册的类列表。

返回值:

类型 说明
std::forward_list<ClassBase*>& 类列表引用。

Binding::RegisterClass

static void RegisterClass(ClassBase* xlass)

注册 C++ 类。

参数:

参数名 类型 必填 说明
xlass ClassBase* Y 类基类指针。

Binding::GetEnumerationList

static std::forward_list<EnumerationBase*>& GetEnumerationList()

获取已注册的枚举列表。

返回值:

类型 说明
std::forward_list<EnumerationBase*>& 枚举列表引用。

Binding::RegisterEnumeration

static void RegisterEnumeration(EnumerationBase* enumeration)

注册枚举类型。

参数:

参数名 类型 必填 说明
enumeration EnumerationBase* Y 枚举基类指针。

Binding::RegisterJSFunction

static int RegisterJSFunction(const std::string& name, std::unique_ptr<JSFunction> func)

注册 ETS 函数回调。

参数:

参数名 类型 必填 说明
name std::string Y 函数名,用于 Native 索引。
func std::unique_ptr Y ETS 函数指针。

返回值:

类型 说明
int 当前被绑定的函数下标索引。

Binding::UnRegisterJSFunction

static int UnRegisterJSFunction(const std::string& name)

注销 ETS 函数回调。

参数:

参数名 类型 必填 说明
name std::string Y 需要注销的函数名。

返回值:

类型 说明
int 0表示成功,-1表示失败。

Binding::AssociationClassExtend

static void AssociationClassExtend(const char *name, const char *parentName)

关联类的继承关系。

参数:

参数名 类型 必填 说明
name const char* Y 子类名。
parentName const char* Y 父类名。

Binding::GetAssociationClassExtend

static std::forward_list<const char *> GetAssociationClassExtend(const char *name)

获取类的所有父类。

参数:

参数名 类型 必填 说明
name const char* Y 类名。

返回值:

类型 说明
std::forward_list<const char *> 父类名列表。

Binding::GetJSFunctionMap

static std::map<std::string, std::unique_ptr<JSFunction>>& GetJSFunctionMap()

获取 ETS 函数映射表。

返回值:

类型 说明
std::map<std::string, std::unique_ptr>& 函数名到函数指针的映射表引用。

示例:

#include <aki/binding.h>

// 获取当前 NAPI 环境
napi_env env = aki::Binding::GetScopedEnv();

// 注册 ETS 函数回调
aki::Binding::RegisterJSFunction("callbackName", std::make_unique<JSFunction>(func));

aki::Version

Version 类提供 AKI 库的版本信息查询功能。

Version::GetVersion

static const char* GetVersion()

返回 AKI 版本字符串。

返回值:

类型 说明
const char* 版本字符串(如 "1.2.26")。

示例:

#include <aki/version.h>

const char* version = aki::Version::GetVersion();
// 输出: "1.2.26"

aki::ScopedLogMessage

ScopedLogMessage 是 RAII 风格的日志消息类,在构造时记录日志上下文信息,在析构时输出日志。通常通过宏来使用。

ScopedLogMessage 构造函数

ScopedLogMessage(LogLevel level, const char* file, int line, const char* condition)

参数:

参数名 类型 必填 说明
level LogLevel Y 日志级别。
file const char* Y 源文件名。
line int Y 源文件行号。
condition const char* Y 条件表达式字符串。

ScopedLogMessage::stream

std::ostream& stream()

获取输出流,用于写入日志内容。

返回值:

类型 说明
std::ostream& 输出流引用。

日志级别定义:

级别常量 说明
LOG_DEBUG 0 调试信息
LOG_INFO 1 一般信息
LOG_WARNING 2 警告信息
LOG_ERROR 3 错误信息
LOG_FATAL 4 致命错误

主要宏定义:

功能描述
AKI_LOG(level) 输出指定级别的日志
AKI_DLOG(level) 仅在 DEBUG 模式输出日志
AKI_CHECK(condition) 断言检查,失败时输出 FATAL 日志并终止
AKI_DCHECK(condition) 仅在 DEBUG 模式的断言检查

示例:

#include <aki/logging/logging.h>

AKI_LOG(INFO) << "Function called successfully";
AKI_LOG(ERROR) << "Failed to process data";
AKI_CHECK(ptr != nullptr) << "Pointer should not be null";

aki::NapiOverloader

NapiOverloader 类用于实现 C++ 函数/构造函数的重载支持,根据传入参数的数量选择正确的方法调用。

NapiOverloader::Wrapper

static napi_value Wrapper(napi_env env, napi_callback_info info)

重载函数包装器,根据参数数量选择正确的重载方法。

参数:

参数名 类型 必填 说明
env napi_env Y NAPI 环境句柄。
info napi_callback_info Y 回调信息。

返回值:

类型 说明
napi_value 调用结果。

NapiOverloader::CreateValue

static napi_value CreateValue(napi_env env, napi_callback_info info)

创建值的重载处理。

参数:

参数名 类型 必填 说明
env napi_env Y NAPI 环境句柄。
info napi_callback_info Y 回调信息。

返回值:

类型 说明
napi_value 创建的值。

NapiOverloader::AddGroup

static int32_t AddGroup()

添加一个新的重载组。

返回值:

类型 说明
int32_t 重载组ID。

NapiOverloader::AddMethod

static void AddMethod(int32_t groupId, int32_t arity, FuncPtr method)

向指定组添加方法。

参数:

参数名 类型 必填 说明
groupId int32_t Y 重载组ID。
arity int32_t Y 参数数量。
method FuncPtr Y 方法指针。

NapiOverloader::GetGroup

static OverloadGroup& GetGroup(int32_t groupId)

获取指定ID的重载组。

参数:

参数名 类型 必填 说明
groupId int32_t Y 重载组ID。

返回值:

类型 说明
OverloadGroup& 重载组引用。

NapiOverloader::SetExternalWrapper

static void SetExternalWrapper(int32_t groupId, napi_callback externalWrapper)

设置外部类型包装器。

参数:

参数名 类型 必填 说明
groupId int32_t Y 重载组ID。
externalWrapper napi_callback Y 外部类型包装器回调。

NapiOverloader::GetOverloaderGroups

static std::vector<OverloadGroup>& GetOverloaderGroups()

获取所有重载组。

返回值:

类型 说明
std::vector& 重载组列表引用。

使用场景: 内部用于支持 JSBIND_CONSTRUCTOR<> 的多参数重载。

aki::Persistent

Persistent 类是对 napi_ref 的封装,用于持有 ETS 值的持久引用。当需要在多个 NAPI 调用之间保持 ETS 对象的引用时使用。

Persistent 构造函数

Persistent()
explicit Persistent(napi_value value)
Persistent(const Persistent& other)
Persistent(Persistent&& other) noexcept

参数:

参数名 类型 必填 说明
value napi_value Y 构造持久引用的 napi_value(仅用于第二种构造函数)。
other Persistent Y 用于拷贝或移动构造的源对象。

Persistent::IsValid

bool IsValid() const

检查引用是否有效。

返回值:

类型 说明
bool true表示引用有效,false表示引用无效。

Persistent::GetValue

napi_value GetValue() const

从持久引用获取 napi_value。

返回值:

类型 说明
napi_value 持有的 napi_value 值。

示例:

#include <aki/persistent/persistent.h>

Persistent persistent(jsValue);

if (persistent.IsValid()) {
    napi_value value = persistent.GetValue();
}

aki::NapiValueBase

NapiValueBase 是 NAPI 值类型的基础实现类,提供 ETS 值的类型检查和类型转换功能。

NapiValueBase 构造函数

NapiValueBase() = default
NapiValueBase(napi_env env, napi_value value)
virtual ~NapiValueBase() = default

参数:

参数名 类型 必填 说明
env napi_env Y NAPI 环境句柄。
value napi_value Y napi_value 值。

类型检查方法:

方法签名 功能描述
static bool CheckUndefinedType(napi_env env, napi_value value) 检查是否为 undefined
static bool CheckNullType(napi_env env, napi_value value) 检查是否为 null
static bool CheckBoolType(napi_env env, napi_value value) 检查是否为布尔类型
static bool CheckNumberType(napi_env env, napi_value value) 检查是否为数字类型
static bool CheckStringType(napi_env env, napi_value value) 检查是否为字符串类型
static bool CheckObjectType(napi_env env, napi_value value) 检查是否为对象类型
static bool CheckArrayType(napi_env env, napi_value value) 检查是否为数组类型
static bool CheckFunctionType(napi_env env, napi_value value) 检查是否为函数类型

参数:

参数名 类型 必填 说明
env napi_env Y NAPI 环境句柄。
value napi_value Y 待检查的 napi_value。

返回值:

类型 说明
bool true表示类型匹配,false表示不匹配。

类型判断方法:

方法签名 功能描述
bool IsUndefined() const override 判断是否为 undefined
bool IsNull() const override 判断是否为 null
bool IsBool() const override 判断是否为布尔值
bool IsNumber() const override 判断是否为数字
bool IsString() const override 判断是否为字符串
bool IsObject() const override 判断是否为对象
bool IsArray() const override 判断是否为数组
bool IsFunction() const override 判断是否为函数

返回值:

类型 说明
bool true表示类型匹配,false表示不匹配。

值获取方法:

方法签名 功能描述
bool GetBool() const override 获取布尔值
uint8_t GetUint8() const override 获取 uint8_t 值
int8_t GetInt8() const override 获取 int8_t 值
uint16_t GetUint16() const override 获取 uint16_t 值
int16_t GetInt16() const override 获取 int16_t 值
int32_t GetInt() const override 获取 int32_t 值
uint32_t GetUInt() const override 获取 uint32_t 值
uint64_t GetUInt64() const override 获取 uint64_t 值
int64_t GetInt64() const override 获取 int64_t 值
float GetFloat() const override 获取 float 值
double GetDouble() const override 获取 double 值
std::string GetString() const override 获取字符串值
napi_value GetNapiValue() const 获取原始 napi_value
void* GetDataReference() override 获取对象引用
ArrayBuffer GetArrayBuffer() const override 获取 ArrayBuffer
Promise GetPromise() override 获取 Promise

返回值:

类型 说明
对应类型 转换后的 C++ 类型值。

混合开发

AKI 支持与 Node-API 混合开发。接口 aki::JSBind::BindSymbols 用于绑定使用 AKI 的 Native 符号表给指定的 napi_value 对象。

如下示例:

examples/ohos/4_hybrid_napi/entry/src/main/hello.cpp

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        ...
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    
    exports = aki::JSBind::BindSymbols(env, exports); // aki::JSBind::BindSymbols 函数传入 ETS 对象绑定符号
    return exports;
}
EXTERN_C_END

关于混淆

  • 代码混淆,请查看代码混淆简介
  • 如果希望aki在代码混淆过程中不会被混淆,需要在混淆规则配置文件obfuscation-rules.txt中添加相应的排除规则:
-keep
./oh_modules/@ohos/aki

具体用例参考混淆配置示例

Benchmark

  • IDE:DevEco Studio NEXT Developer Beta6 5.0.3.706
  • SDK:Command Line Tools 5.0.3.706

API接口压测,当前采用了OHOS上单元测试框架的数据驱动能力,详见benchmark

API 调用次数 AKI (ms) Node-API (ms)
bool (*)() 10000 0.0032 0.0031
string (*)(string) 10000 0.0058 0.0057
void (*)( std::function ) 10000 0.0667 0.0176
void (*)( aki::Callback ) 10000 0.0178
void (*)( aki::SafetyCallback ) 10000 0.0664

常见问题

目录结构

aki/
├── include/                    # 公开头文件目录
│   └── aki/                   # AKI 核心头文件
│       ├── jsbind.h           # 主要绑定宏和 API
│       ├── function.h         # 函数绑定支持
│       ├── binding.h          # 绑定基础设施
│       ├── value.h            # aki::Value 实现
│       ├── promise.h          # Promise 支持
│       └── asyncworker/       # 异步工作相关
├── src/                        # 源代码目录
│   ├── binding.cpp            # 核心绑定实现
│   ├── jsbind.cpp             # JSBind 实现
│   ├── value.cpp              # 值转换实现
│   └── ...                    
├── example/ohos/              # OpenHarmony 示例
│   ├── 1_helloworld/          # 基础 Hello World 示例
│   ├── 2_applicationInfo/     # 应用信息示例
│   ├── 3_callback/            # 回调示例
│   ├── 4_hybrid_napi/         # 与 Node-API 混合开发
│   ├── 5_bind_from_js/        # JS 线程安全绑定
│   ├── 15_aki_value/          # aki::Value 使用
│   ├── 16_promise/            # Promise 使用
│   ├── 19_class_extend/       # 类继承示例
│   ├── 26_extern_work_scene/  # 代码混淆配置示例
│   └── ...                    
├── test/                       # 单元测试目录
│   ├── callback/              # 回调测试
│   ├── class/                 # 类绑定测试
│   ├── array/                 # 数组类型测试
│   ├── map/                   # Map 类型测试
│   └── ...                    
├── doc/                        # 文档目录
│   ├── quick-start_zh.md      # 快速开始(中文)
│   ├── quick-start.md         # 快速开始(英文)
│   ├── type-conversion_zh.md  # 类型转换完整说明
│   ├── bind_class.md          # 类绑定详细说明
│   ├── bind_enum_zh.md        # 枚举绑定说明
│   └── ...                    
├── benchmark/                  # 性能基准测试
├── CMakeLists.txt             # 主构建文件
├── README_zh.md               # 中文 README (本文件)
├── README.md                  # 英文 README
├── LICENSE                    # 开源协议文件
├── CHANGELOG.md               # 版本变更日志
└── OAT.xml                    # 开源协议遵循声明

贡献代码

使用过程中发现任何问题都可以提交Issue,当然,也非常欢迎提交PR

开源协议

本项目遵循Apache-2.0 License