北向應(yīng)用集成三方庫——Napi接口封裝工具aki

AKI 項目介紹

AKI (Alpha Kernel Interacting) 是一款邊界性編程體驗友好的ArkTs FFI開發(fā)框架,針對OpenHarmony Native開發(fā)提供JS與C/C++跨語言訪問場景解決方案。支持極簡語法糖使用方式,一行代碼完成JS與C/C++的無障礙跨語言互調(diào),所鍵即所得。

優(yōu)勢

  1. 極簡使用,解耦FFI代碼與業(yè)務(wù)代碼,友好的邊界性編程體驗;
  2. 提供完整的數(shù)據(jù)類型轉(zhuǎn)換、函數(shù)綁定、對象綁定、線程安全等特性;
  3. 支持JS & C/C++互調(diào);
  4. 支持與Node-API嵌套使用;

已測試兼容環(huán)境

  • OpenHarmony(API 10) SDK (4.0.9.6): 通過
  • DevEco Studio (4.0.0.400): 通過
  • OpenHarmony(API 9) SDK (3.2.12.2): 通過
  • DevEco Studio (3.1.0.500): 通過

1 依賴配置(2選1)

  • 源碼依賴(推薦)

    指定cpp路徑下(如:項目根路徑/entry/src/main/cpp)

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

    CMakeLists.txt添加依賴(假定編譯動態(tài)庫名為: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添加依賴(假定編譯動態(tài)庫名為:libhello.so):

    set(AKI_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules/@ohos/aki) # 設(shè)置AKI根路徑
    set(CMAKE_MODULE_PATH ${AKI_ROOT_PATH})
    find_package(Aki REQUIRED)
    
    ...
    
    target_link_libraries(hello PUBLIC Aki::libjsbind) # 鏈接二進(jìn)制依賴 & 頭文件
    

2 用戶自定義業(yè)務(wù)

用戶業(yè)務(wù) C++ 代碼 hello.cpp:

全程無感node-API

#include <string>

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

3 使用 AKI

使用JSBind工具宏聲明需要被綁定的類、函數(shù):

#include <aki/jsbind.h>

// Step 1 注冊 AKI 插件
JSBIND_ADDON(hello) // 注冊 AKI 插件名: 即為編譯*.so名稱,規(guī)則與NAPI一致

// Step 2 注冊 FFI 特性
JSBIND_GLOBAL()
{
  JSBIND_FUNCTION(SayHello);
}

4 編譯構(gòu)建使用

OpenHarmony工程代碼調(diào)用:

import aki from 'libhello.so' // 工程編譯出來的*.so

aki.SayHello("hello world");

JSBind 語法糖

插件注冊

JSBIND_ADDON(addonName)

使用JSBIND_ADDON注冊O(shè)penHarmony Native 插件,可從 JavaScript import 導(dǎo)入插件。

參數(shù):

參數(shù)名 類型 必填 說明
addonName - Y 注冊的OpenHarmony native 插件名,可從 JavaScript import lib${addonName}.so 導(dǎo)入插件,插件名必須符合函數(shù)命名規(guī)則。

示例:

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

JSBIND_ADDON(addon0)

  • JavaScript
import addon from 'libaddon0.so' // 插件名為:addon0

JSBIND_ADDON_X(addonName constructorAlias)

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

參數(shù):

參數(shù)名 類型 必填 說明
addonName - Y 注冊的OpenHarmony native 插件名,可從 JavaScript import lib${addonName}.so 導(dǎo)入插件,插件名可包含特殊符號,如:'-'。
constructorAlias - Y 插件預(yù)構(gòu)造函數(shù)名,只需填寫符合函數(shù)命名規(guī)則名稱即可,無其他特殊含義

示例:

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

JSBIND_ADDON(hello-world, HelloWorld)

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

綁定全局函數(shù)

JSBIND_GLOBAL

用于圈定需要綁定的全局函數(shù) scope。

JSBIND_FUNCTION(func, alias)

JSBIND_GLOBAL作用域下使用JSBIND_FUNCTION綁定 C++ 全局函數(shù)后,可從 JavaScript 直接調(diào)用。

  • 調(diào)度線程為 JS 線程;

參數(shù):

參數(shù)名 類型 必填 說明
func 函數(shù)指針 Y 被綁定的C++函數(shù)指針,當(dāng)alias未被指定時,JavaScriptC++函數(shù)名相同。
alias string N 函數(shù)別名

示例:

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

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

JSBIND_GLOBAL()
{
    JSBIND_FUNCTION(SayHello);
}

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

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

JSBIND_PFUNCTION(func, alias)

使用JSBIND_PFUNCTION綁定 C++ 全局函數(shù)后,從 JavaScript 使用同Promise方式相同的異步調(diào)用。

  • 調(diào)度線程為工作線程,由 ArkCompiler Runtime 決定;

參數(shù):

參數(shù)名 類型 必填 說明
func 函數(shù)指針 Y 被綁定的C++函數(shù)指針。
alias string N 函數(shù)別名

示例:

  • C++

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

JSBIND_GLOBAL() {
    JSBIND_PFUNCTION(AsyncTaskReturnInt);
}

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

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

綁定類/結(jié)構(gòu)體

AKI 提供 JSBIND_CLASS 對 C++ 類/結(jié)構(gòu)體進(jìn)行綁定,在JSBIND_CLASS作用域下可綁定:類構(gòu)造函數(shù)、類成員函數(shù)、類成員屬性的類特性。

JSBIND_CLASS(class)

參數(shù):

參數(shù)名 類型 必填 說明
class class/struct Y 被綁定的C++類對象/結(jié)構(gòu)體JavaScriptC++類名相同。

JSBIND_CONSTRUCTOR<T>()

JSBIND_CLASS作用域下使用綁定 C++ 類/結(jié)構(gòu)體構(gòu)造函數(shù),其中為了支持多態(tài),可通過類型模板指定構(gòu)造函數(shù)參數(shù)類型。

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

參數(shù):

參數(shù)名 類型 必填 說明
T any N 構(gòu)造函數(shù)參數(shù)類型,可變類型參數(shù)。

示例:

  • 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);
  • JavaScript
import aki from 'libhello.so' // 插件名

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

綁定類成員函數(shù)

JSBIND_METHOD(method)

AKI 使用 JSBIND_METHOD 對C++ 的3種類成員函數(shù)進(jìn)行綁定:類靜態(tài)函數(shù)、類成員函數(shù)、const 類成員函數(shù)。

  • JSBIND_METHOD 需要在JSBIND_CLASS的作用域下;
  • 調(diào)度線程為 JS 線程;

參數(shù):

參數(shù)名 類型 必填 說明
method R (C::*)(P...) Y 同時支持類靜態(tài)函數(shù)、類成員函數(shù)、const 類成員函數(shù)。

示例:

使用 AKI 對C++類成員函數(shù)綁定

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

例:JavaScript 側(cè)調(diào)用綁定的C++類成員函數(shù)

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) // 靜態(tài)方法

JSBIND_PMETHOD(method)

JSBIND_PMETHOD用于綁定 C++ 類成員函數(shù),從 JavaScript 使用同Promise方式相同的異步調(diào)用。

  • 調(diào)度線程為工作線程,由 ArkCompiler Runtime 決定;

參數(shù):

參數(shù)名 類型 必填 說明
method 類成員函數(shù)指針 Y 被綁定的C++類成員函數(shù)指針。

示例:

  • 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);
  • JavaScript
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++ 的類成員屬性、類成員屬性訪問器進(jìn)行綁定

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

參數(shù):

參數(shù)名 類型 必填 說明
property T Y 類成員屬性名。

示例:

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

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

private:
    double value_;
} // TestObject

JSBIND_CLASS(TestObject)
{
    JSBIND_CONSTRUCTOR<double>();
    JSBIND_PROPERTY(value);
}
  • JavaScript
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++ 的類成員屬性進(jìn)行監(jiān)聽

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

  • 調(diào)度線程為 JS 線程;

參數(shù):

參數(shù)名 類型 必填 說明
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);
}
  • JavaScript
import aki from 'libhello.so' // 插件名

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

綁定枚舉類型

JSBind語法糖JSBIND_ENUMJSBIND_ENUM_VALUE支持綁定 C/C++ 枚舉類型,映射為 JavaScript 的Number類型。

  • C/C++側(cè)默認(rèn)枚舉類型為POD中的int32_t;
  • JavaScript側(cè)對應(yīng)的枚舉類型屬性為readonly

JSBIND_ENUM(enum)

參數(shù):

參數(shù)名 類型 必填 說明
enum enum Y 被綁定的C++枚舉類型。

JSBIND_ENUM_VALUE(value)

參數(shù):

參數(shù)名 類型 必填 說明
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);
  • JavaScript
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);
}

線程安全函數(shù)

使用AKI的線程安全特性,綁定 JavaScript 的業(yè)務(wù)函數(shù)后,可由native直接調(diào)用。

  • 線程安全:使用AKI線程安全綁定的 JavaScript 函數(shù)是線程安全的,可在非JS線程直接調(diào)用。最終會由框架調(diào)度JS線程執(zhí)行業(yè)務(wù);
  • 阻塞式調(diào)用:C++ 觸發(fā)調(diào)用 JavaScript 函數(shù)的調(diào)用是阻塞式的,對于在JS線程執(zhí)行業(yè)務(wù)這點沒有疑義。但當(dāng)C++觸發(fā) JavaScript 業(yè)務(wù)調(diào)用的線程是非JS線程時,就存在跨線程任務(wù)調(diào)度。此時由框架進(jìn)行了阻塞式調(diào)用,即 C++ 會等待 JavaScript 函數(shù)執(zhí)行結(jié)束后返回;

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

在 JavaScript 使用 JSBind.bindFunction 綁定 JavaScript 全局函數(shù)后,可從 C++ 直接調(diào)用。

參數(shù):

參數(shù)名 類型 必填 說明
name string Y 指定綁定的JavaScript函數(shù)名,用于Native索引。
func function Y 被綁定的JavaScript函數(shù)

返回值:

類型 說明
number 當(dāng)前被綁定的函數(shù)下標(biāo)索引
// name: 指定函數(shù)名,func: JavaScript 全局函數(shù)
libAddon.JSBind.bindFunction(name: string, func: Function);

C++ 使用aki::JSBind::GetJSFunction獲取指定 JavaScript 函數(shù)句柄后,使用Invoke觸發(fā)調(diào)用

auto jsFunc = aki::JSBind::GetJSFunction("xxx"); // 獲取指定函數(shù)句柄
auto result = jsFunc->Invoke<T>(...); // 調(diào)用JavaScript函數(shù),Invoke<T>指定返回值類型
  • JavaScript
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() {
    // 索引 JS 函數(shù)句柄
    auto jsFunc = aki::JSBind::GetJSFunction("sayHelloFromJS");

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

類型轉(zhuǎn)換

JavaScript C++
Boolean bool
Number uint8_t, int8_t, uint16_t, int16_t, short, int32, uint32, int64, 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>
ArrayBuffer,
TypedArray
aki::ArrayBuffer
Promise JSBIND_PFUNCTION, JSBIND_PMETHOD
any aki::Value, napi_value

NOTE: 帶有陰影部分的表示已支持

const char* 是以引用方式傳遞參數(shù),如遇到異步操作,請使用傳值方式:std::string

Boolean

如下示例,開發(fā)者直接聲明函數(shù)入?yún)⒓胺祷刂殿愋停褂?code>AKI綁定后,框架自適應(yīng)對 C/C++ 的 bool 及 JavaScript 的 Boolean 類型進(jìn)行轉(zhuǎn)化。

示例:

  • C++

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

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

Number

如下示例,開發(fā)者直接聲明函數(shù)入?yún)⒓胺祷刂殿愋停褂?code>AKI綁定后,框架自適應(yīng)對 C/C++ 的 uint8_t, int8_t, uint16_t, int16_t, short, int32, uint32, int64, float, double, enum 及 JavaScript 的 Number 類型進(jìn)行轉(zhuǎn)化。

  • float: 浮點型轉(zhuǎn)換時存在精度丟失,對于高精度場景,請使用 double

示例:

  • C++

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

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

String

如下示例,開發(fā)者直接聲明函數(shù)入?yún)⒓胺祷刂殿愋停褂?code>AKI綁定后,框架自適應(yīng)對 C/C++ 的 const char*, std::string 及 JavaScript 的 String 類型進(jìn)行轉(zhuǎn)化。

  • const char* 是以引用方式傳遞參數(shù),如遇到異步操作,請使用傳值方式: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)
    
  • JavaScript

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

Array

如下示例,開發(fā)者直接聲明函數(shù)入?yún)⒓胺祷刂殿愋停褂?code>AKI綁定后,框架自適應(yīng)對 C/C++ 的 std::vector<T>, std::array<T, N> 及 JavaScript 的 [] 類型進(jìn)行轉(zhuǎn)化。

  • 數(shù)組類型僅支持同種類型的數(shù)組聲明;

示例:

  • 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)
    
  • JavaScript

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

ArrarBuffer

二進(jìn)制數(shù)據(jù)緩沖區(qū)ArrayBuffer, TypedArray 是 JavaScript AKI 提供了內(nèi)建結(jié)構(gòu)體:aki::ArrayBuffer用來支持該特性:

  • GetData()* 獲取 ArrayBuffer 數(shù)組緩沖區(qū)地址,aki::ArrayBuffer 本身不申請數(shù)據(jù)內(nèi)存,data 都來源于JavaScript引擎分配的內(nèi)存,也無需做內(nèi)存生命周期管理,禁止對該內(nèi)存進(jìn)行危險的釋放

  • GetLength() 獲取 ArrayBuffer 數(shù)組緩沖區(qū)長度,以單字節(jié)為計量單位。

  • GetTyped() 獲取 ArrayBuffer 數(shù)組緩沖區(qū)的類型化類型。

  • GetCount() 獲取 ArrayBuffer 數(shù)組緩沖區(qū)的類型化數(shù)據(jù)元素個數(shù)。

示例:

  • 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;
}
  • JavaScript
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

JavaScript支持使用JsonObject表示key-value結(jié)構(gòu)的數(shù)據(jù)類型,如:

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

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

  • std::map<std::string, T>對應(yīng)的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);
}
  • JavaScript
import libmap_for_object from 'libmap_for_object.so'

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

Function

Function是JS的一種基本數(shù)據(jù)類型,當(dāng)JS傳入Function作為參數(shù)時,Native可在適當(dāng)?shù)臅r機(jī)調(diào)用觸發(fā)回調(diào)。AKI 支持如下3中C++數(shù)據(jù)類型作為參數(shù)處理回調(diào):

  • aki::Callback<R (P...)>:指定回調(diào)類型為R (*)(P...)高性能回調(diào)。非線程安全,禁止在非JS線程使用,否則會發(fā)生異常;
  • aki::SafetyCallback<R (P...)>:指定回調(diào)類型為R (*)(P...)的線程安全回調(diào)。因為需要創(chuàng)建線程安全資源,所以性能不如aki::Callback;
  • std::function<R (P...)>:用法與aki::SafetyCallback一致;

對象引用&指針

C++ 對象作為參數(shù)和返回類型,在 C++ & JavaScript 代碼中可以使用如下形式進(jìn)行傳遞:

  • 值傳遞;
  • 引用(T&)與指針(T*)傳遞;

API參考

napi_env 獲取

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

線程安全函數(shù),用于獲取當(dāng)前線程的 napi_env 對象。當(dāng)在非 JS 線程調(diào)用時,返回 nullptr。

參數(shù):

參數(shù)名 類型 必填 說明
key string Y 需要讀取的屬性名。

示例:

// 在 JS 線程執(zhí)行
napi_value obj;
napi_env env = aki::JSBind::GetScopedEnv();
napi_create_object(env, &obj);

TaskRunner 任務(wù)調(diào)度器

TaskRunner提供JS線程的任務(wù)調(diào)度器,開發(fā)人員可以很方便地往JS線程PostTask

JSBind.initTaskRunner(name: string)

JS側(cè)的靜態(tài)函數(shù),用于初始化對應(yīng)JS線程的任務(wù)調(diào)度器。

參數(shù):

參數(shù)名 類型 必填 說明
runnerName string Y 任務(wù)調(diào)度器別名。

示例:

import libAddon from "libaki.so"

libAddon.JSBind.initTaskRunner("name");

PostTask

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

靜態(tài)函數(shù),往指定任務(wù)調(diào)度器,投遞任務(wù)。

參數(shù):

參數(shù)名 類型 必填 說明
runnerName string Y 指定任務(wù)調(diào)度器,需先使用JSBind.initTaskRunner初始化任務(wù)調(diào)度器。
task Closure Y 任務(wù)表達(dá)式: std::function<void ()>。

示例:


void foo ()
{
    aki::TaskRunner::PostTask("main", [] () {
      // 在 JS 線程執(zhí)行
      // do something
    });
}

aki::Value v1.2.0

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

aki::Value::FromGlobal

static Value FromGlobal(const char* key = nullptr)

用于獲取 JS 側(cè)globalThis下的屬性。

參數(shù):

參數(shù)名 類型 必填 說明
key string Y 需要讀取的屬性名。

返回值:

類型 說明
aki::Value 對應(yīng)屬性的 JS 對象句柄。

示例:

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

aki::Value::As

template<typename T>
T As() const;

模板函數(shù),用于將 JS 對象轉(zhuǎn)化為 C/C++ 指定數(shù)據(jù)類型

參數(shù):

參數(shù)名 類型 必填 說明
T any Y 需要被轉(zhuǎn)化的 C/C++ 數(shù)據(jù)類型。

返回值:

類型 說明
T 對應(yīng)類型的值。

示例:

  value.As<bool>(); // 將 JS 對象 value 轉(zhuǎn)化為 bool
  value.As<int>(); // 將 JS 對象 value 轉(zhuǎn)化為 int
  value.As<std::string>(); // 將 JS 對象 value 轉(zhuǎn)化為 string

aki::Value::GetHandle

napi_value GetHandle() const

用于獲取 JS 對象的 napi_value 句柄。

返回值:

類型 說明
napi_value JS 對象的 napi_value 句柄。

aki::Value::CallMethod

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

調(diào)用 JS 對象的成員函數(shù)。

參數(shù):

參數(shù)名 類型 必填 說明
name string Y 函數(shù)名。
args any N 成員函數(shù)接收的參數(shù)。

返回值:

類型 說明
aki::Value 返回泛型對象。

示例:

  // value 映射為 JS 數(shù)組對象 let value = ['aki'];
  // 調(diào)用 value.push('jsbind');
  value.CallMethod("push", "jsbind");

aki::Value::operator[]

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

aki::Value對象的下標(biāo)運(yùn)算符

參數(shù):

參數(shù)名 類型 必填 說明
key string Y 屬性名下標(biāo)。
index size_t Y 數(shù)組下標(biāo)。

返回值:

類型 說明
aki::Value 返回泛型對象。

示例:

  // value 映射為 JS 數(shù)組對象 let value = ['aki', 'jsbind'];
  // 訪問下標(biāo)為0的值:'aki';
  aki::Value str = value[0]; // str = "aki"

  // 調(diào)用 JSON.stringify(...);
  aki::Value::FromGlobal("JSON")["stringify"](...);

aki::Value::operator()

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

aki::Value對象的函數(shù)調(diào)用運(yùn)算符

參數(shù):

參數(shù)名 類型 必填 說明
args any N 函數(shù)所接收入?yún)ⅰ?/td>

返回值:

類型 說明
aki::Value 返回泛型對象。

示例:

  // 調(diào)用 JSON.parse({'aki': 'jsinbd'});
  aki::Value::FromGlobal("JSON")["parse"]({"aki": "jsinbd"});

aki::Value::Set

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

用于給aki::Value泛型對象屬性設(shè)值。

參數(shù):

參數(shù)名 類型 必填 說明
key string Y 屬性名。
value any Y 屬性值。

示例:

  // value 為 JS 對象;
    value.Set("name", "aki");

aki::Value::NewObject

static Value NewObject();

創(chuàng)建aki::Value泛型對象。

返回值:

類型 說明
aki::Value 返回泛型對象。

示例:

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

aki::Value::IsUndefined

bool IsUndefined() const

判斷 JS 對象類型是否為undefined

返回值:

類型 說明
bool true or false。

aki::Value::IsNull

bool IsNull() const

判斷 JS 對象類型是否為null

返回值:

類型 說明
bool true or false。

aki::Value::IsBool

bool IsBool() const

判斷 JS 對象類型是否為boolean

返回值:

類型 說明
bool true or false。

aki::Value::IsNumber

bool IsNumber() const

判斷 JS 對象類型是否為number

返回值:

類型 說明
bool true or false。

aki::Value::IsString

bool IsString() const

判斷 JS 對象類型是否為string

返回值:

類型 說明
bool true or false。

aki::Value::IsArray

bool IsArray() const

判斷 JS 對象類型是否為數(shù)組[]

返回值:

類型 說明
bool true or false。

aki::Value::IsFunction

bool IsFunction() const

判斷 JS 對象類型是否為function

返回值:

類型 說明
bool true or false。

C/C++ 調(diào)用 @ohos.bundle.bundleManager (bundleManager模塊)特性

示例:

  • 期望在 C++ 調(diào)用如下@ohos.bundle.bundleManager (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++代碼實現(xiàn)上述功能

      /* 要求在ArkTS側(cè)執(zhí)行如下代碼:
      * import bundleManager from '@ohos.bundle.bundleManager';
      * globalThis.bundleManager = bundleManager;
      */
      aki::Value bundleManager = aki::Value::FromGlobal("bundleManager");
      
      /* 如下 C++ 代碼等同于 JS 代碼:
      * 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);
    

aki::ArrayBuffer

constructor

  • 當(dāng)在非 JS 線程使用 aki::ArrayBuffer,需要關(guān)注數(shù)據(jù)字節(jié)流生命周期,并考慮是否需要結(jié)合Commit()函數(shù)使用。
ArrayBuffer(uint8_t* ptr, size_t len, Typed typed = BUFF)

參數(shù):

參數(shù)名 類型 必填 說明
ptr uint8_t* Y 構(gòu)造 ArrayBuffer 的數(shù)據(jù)字節(jié)流內(nèi)存地址。
len size_t Y 構(gòu)造 ArrayBuffer 的數(shù)據(jù)字節(jié)流內(nèi)存長度。
typed aki::ArrayBuffer::Typed N 構(gòu)造的 ArrayBuffer | TypedArray 類型,默認(rèn)為 ArrayBuffer。

示例:

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

GetData

uint8_t* GetData()

獲取 ArrayBuffer 的數(shù)據(jù)字節(jié)流內(nèi)存地址。

返回值:

類型 說明
uint8_t* ArrayBuffer 的數(shù)據(jù)字節(jié)流內(nèi)存地址。

GetLength

size_t GetLength()

獲取 ArrayBuffer 的數(shù)據(jù)字節(jié)流內(nèi)存長度。

返回值:

類型 說明
size_t ArrayBuffer 的數(shù)據(jù)字節(jié)流內(nèi)存長度。

Commit

void Commit()

當(dāng)在非 JS 線程使用 ArrayBuffer 時,如果數(shù)據(jù)字節(jié)流的內(nèi)存生命周期在 ArrayBuffer 使用前結(jié)束,則需要暫存。

返回值:

類型 說明
size_t ArrayBuffer 的數(shù)據(jù)字節(jié)流內(nèi)存長度。

示例:

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

AKI hybrid Node-API 混合開發(fā)

AKI 支持與 Node-API 混合開發(fā)。接口 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 函數(shù)傳入 js 對象綁定符號
    return exports;
}
EXTERN_C_END

Benchmark

  • IDE: DevEco Studio 3.1.1.130
  • SDK:3.2.10.6

API接口壓測,當(dāng)前采用了OHOS上單元測試框架的數(shù)據(jù)驅(qū)動能力,詳見benchmark

寫在最后

如果你覺得這篇內(nèi)容對你還蠻有幫助,我想邀請你幫我三個小忙

  • 點贊,轉(zhuǎn)發(fā),有你們的 『點贊和評論』,才是我創(chuàng)造的動力。
  • 關(guān)注小編,同時可以期待后續(xù)文章ing??,不定期分享原創(chuàng)知識。
  • 想要獲取更多完整鴻蒙最新學(xué)習(xí)知識點,請移步前往小編:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容