• Home
Name Date Size #Lines LOC

..--

AppScope/06-May-2025-3432

entry/06-May-2025-2,8622,595

hvigor/06-May-2025-3736

README.mdD06-May-202516.4 KiB542378

build-profile.json5D06-May-20252.1 KiB6969

code-linter.json5D06-May-2025989 3434

hvigorfile.tsD06-May-2025874 215

oh-package.json5D06-May-2025840 2524

README.md

1#### AKI 版 NativeC++ 应用示例
2
3##### AKI简介
4
5AKI (Alpha Kernel Interacting) 是一款边界性编程体验友好的ArkTs FFI开发框架,针对OpenHarmony Native开发提供JS与C/C++跨语言访问场景解决方案。支持极简语法糖使用方式,简洁代码逻辑完成JS与C/C++的无障碍跨语言互调。
6
7简单讲,AKI就是对NAPI进行了一层封装,提供对典型应用场景的包装,减轻了用户开发NAPI层的开发负担,具体优点如下:
8
9* 解耦FFI代码与业务代码,友好的边界性编程体验;
10
11* 提供数据类型转换、函数绑定、对象绑定、线程安全等特性;
12
13* 支持JS & C/C++互调
14* 支持与Node-API即NAPI的嵌套使用
15
16
17
18##### AKI代码样例
19
20* Native C/C++ 业务代码:
21
22```c++
23#include <string>
24#include <aki/jsbind.h>
25
26std::string SayHello(std::string msg) { return msg + " too."; }
27
28// Step 1 注册 AKI 插件
29JSBIND_ADDON(hello) // 注册 AKI 插件名: 即为编译*.so名称,规则与NAPI一致
30
31// Step 2 注册 FFI 特性
32JSBIND_GLOBAL() { JSBIND_FUNCTION(SayHello); }
33```
34
35* ArkTS 业务代码:
36
37```typescript
38import aki from 'libentry.so';
39let msg = aki.SayHello("hell to cpp");
40```
41
42参考:https://gitee.com/openharmony-sig/aki
43
44
45
46##### 工程调试
47
48* 使用DevEco Studio Next Release,Build Version: 5.0.3.900, built on October 8, 2024
49
50* 使用5.0.0 Release 镜像,在rk3568上运行测试
51
52* 使用[资源](https://gitee.com/openharmony/napi_generator/releases/download/%E6%B5%8B%E8%AF%95%E7%94%A8%E8%B5%84%E6%BA%90/akitutorial_package.zip)内的文件分别拷贝到对应路径(解压后thirdparty内容拷贝到akitutorials\entry\src\main\cpp\thirdparty下,libs里的内容拷贝到akitutorials\entry\libs下)
53
54* 编译运行
55
56
57
58##### AKI 接口说明
59
60* **binding.h**:提供提供函数的类的注册方法,对应JSBIND_FUNCTION,JSBIND_CLASS
61
62  * c++代码:
63
64    ```c++
65    #include <string>
66    #include <aki/jsbind.h>
67
68    std::string SayHello(std::string msg)
69    {
70        return msg + " too.";
71    }
72
73    JSBIND_GLOBAL()
74    {
75        JSBIND_FUNCTION(SayHello);
76    }
77
78    JSBIND_ADDON(hello);
79    ```
80
81
82
83  * js代码:
84
85    ```js
86    import aki from 'libhello.so' // 插件名
87
88    let message = aki.SayHello("hello world");
89    ```
90
91
92
93* **jsbind.h**:提供将JS方法绑定至C/C++层使用的能力。使用JSBind类,提供bindFunction,unbindFunction方法,支持JS线程安全函数注册和使用。如:JSBind.bindFunction,aki::JSBind::GetJSFunction,如:
94
95  * c++ 代码:
96
97  ```c++
98  #include <string>
99  #include <aki/jsbind.h>
100
101  void DoSomething() {
102      // 索引 JS 函数句柄
103      auto jsFunc = aki::JSBind::GetJSFunction("sayHelloFromJS");
104
105      // Invoke 指定 JS 方法的返回值类型
106      auto result = jsFunc->Invoke<std::string>("hello from C++"); // 可在非JS线程执行
107      // result == "hello from JS"
108  }
109  ```
110
111  * js 代码:
112
113  ```js
114  import libAddon from 'libhello.so' // 插件名
115
116  function sayHelloFromJS (value) {
117    console.log('what do you say: ' + value);
118    return "hello from JS"
119  }
120
121  libAddon.JSBind.bindFunction("sayHelloFromJS", sayHelloFromJS);
122  ```
123
124
125
126* **version.h**: 提供aki版本号,如:
127
128  * c++ 代码:
129
130  ```c++
131  std::string version(aki::Version::GetVersion());
132  ```
133
134
135
136* **value.h**:提供对value类型的转换,即提供通用类型转换(类似napi_value),Value支持string,number,array等类型,也可通过globalThis拿到对应js句柄,在c++测执行对应方法,如:
137
138  * c++ 代码:
139
140  ```c++
141  aki::Value FromGlobalJSONStringify(aki::Value obj) {
142      // 获取js引入的JSON库
143      aki::Value json = aki::Value::FromGlobal("JSON");
144      // 执行JSON.stringify方法将obj输出为json_string
145      return json["stringify"](obj);
146  }
147  ```
148
149  * js 代码:
150
151  ```js
152  let stringify: string = aki.FromGlobalJSONStringify({
153                'name': 'aki',
154                'age': 1});
155  ```
156
157  以上是展示在C++侧获取默认命名空间的方法,还可以自定义,如:
158
159  * c++ 代码:
160
161  ```c++
162  std::string SayHello(std::string msg) {
163      std::string version(aki::Version::GetVersion());
164      aki::Value buf = aki::Value::FromGlobal("buffer");
165      aki::Value bufObj = buf["alloc"](10, "a");
166      aki::Value isBuffer = buf["isBuffer"](bufObj);
167      bool isBuf = isBuffer.As<bool>();
168      std::string res = isBuf ? "true" : "false";
169      return msg + " too." + version + res;
170  }
171  ```
172
173  * js 代码:注意,必须是ts文件,如果ets文件,编译报错不支持globalThis
174
175  ```js
176  import buffer from '@ohos.buffer';
177
178  export class Test {
179    static setBuffer(){
180      globalThis.buffer = buffer;
181    }
182  }
183  ```
184
185
186
187* 异步开发:
188
189  * cpp 代码:
190
191    ```c++
192    static aki::Promise ReturnPromiseResolveLater()
193    {
194        aki::Promise promise;
195
196        std::thread t([promise] () {
197            aki::TaskRunner::PostTask("main", [promise] () {
198                promise.Resolve(1);
199            });
200        });
201        t.detach();
202        return promise;
203    }
204    ```
205
206  * js 代码:
207
208    ```js
209    libPromise.JSBind.initTaskRunner("main");
210    libPromise.ReturnPromiseResolveLater().then((value) => {
211    	console.log('[AKI] ReturnPromiseResolveLater then: ' + value);
212    })
213    ```
214
215
216
217  *
218
219* 混合开发:即用aki也用napi
220
221  * cpp 代码:
222
223    ```c++
224    #include "napi/native_api.h"
225    #include <aki/jsbind.h>
226
227    static napi_value addByNAPI(napi_env env, napi_callback_info info) {
228        ......
229        return sum;
230    }
231
232    EXTERN_C_START
233    static napi_value Init(napi_env env, napi_value exports) {
234        napi_property_descriptor desc[] = {
235            {"addByNAPI", nullptr, addByNAPI, nullptr, nullptr, nullptr, napi_default, nullptr}};
236        napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
237
238        exports = aki::JSBind::BindSymbols(env, exports); // aki::BindSymbols 函数传入 js 对象绑定符号
239        return exports;
240    }
241    EXTERN_C_END
242
243    // napi 方式的native绑定
244    static napi_module demoModule = {
245        .nm_version = 1,
246        .nm_flags = 0,
247        .nm_filename = nullptr,
248        .nm_register_func = Init,
249        .nm_modname = "entry",
250        .nm_priv = ((void *)0),
251        .reserved = {0},
252    };
253
254    extern "C" __attribute__((constructor)) void RegisterHelloModule(void) { napi_module_register(&demoModule); }
255
256    // aki 方式的native绑定
257    std::string SayHello(std::string msg) {
258        return msg + " too.";
259    }
260
261    // Step 1 注册 AKI 插件
262    JSBIND_ADDON(hello) // 注册 AKI 插件名: 即为编译*.so名称,规则与NAPI一致
263
264    // Step 2 注册 FFI 特性
265    JSBIND_GLOBAL() {
266        JSBIND_FUNCTION(SayHello);
267    }
268    ```
269
270
271
272  * js 代码:
273
274    ```js
275    import aki from 'libentry.so';
276
277    let msg: string = aki.SayHello("hell to cpp");
278    hilog.info(0x0000, 'testTag', 'Test SayHello = %{public}s', msg);
279    let res: number = aki.addByNAPI(2, 3);
280    hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', res);
281    ```
282
283
284
285
286
287##### 实现原理:
288
289aki 还是利用node-api技术提供 js 和 cpp 间跨语言的交互接口,主要用于开发针对ArkTS的c/c++插件,帮助开发者在ArkTS(ts)中调用本地代码,c/c++库,同时保证跨版本兼容性;
290
291##### N-API 主要特点
292
2931. **跨版本兼容性**:N-API 提供了一个稳定的 ABI(应用程序二进制接口),这意味着扩展可以在不同版本的 Node.js 上运行,而无需重新编译或修改代码。
2942. **简化开发**:N-API 抽象了一些底层的细节,使得开发者可以专注于应用逻辑,而不必担心 Node.js 内部的实现。
2953. **性能优化**:通过使用本地代码,N-API 可以提高性能,特别是在需要进行大量计算或处理复杂数据结构的情况下。
2964. **安全性**:N-API 提供了一些安全机制,帮助开发者预防常见的内存管理问题,如缓冲区溢出等。
297
298##### 使用场景
299
300- **性能敏感的应用**:例如,大量数据处理、图像处理、加密和解密等。
301- **需要访问底层系统功能**:如文件系统、网络协议等。
302- **重用已有的 C/C++ 库**:如果有成熟的 C/C++ 库,可以通过 N-API 将其封装成 Node.js 模块进行使用。
303
304
305
3061. 依赖库:[CMake参考](https://gitee.com/wshikh/aki/blob/master/src/CMakeLists.txt)
307
308```cmake
309target_link_libraries(${TARGET_NAME} PUBLIC libace_napi.z.so libhilog_ndk.z.so uv)
310```
311
3122. 编译配置:[CMake参考](https://gitee.com/wshikh/aki/blob/master/src/CMakeLists.txt)
313
314```cmake
315//CMakeLists.txt
316option(AKI_BUILDING_SHARED "compile for shared library" ON)
317option(AKI_ENABLE_NAPI "using node-api" ON)
318option(AKI_ENABLE_INSTALL_OHOS "" OFF)
319option(AKI_ENABLE_DECLARATION "" OFF)
320option(AKI_ENABLE_TRACING "DO NOT USE THIS option !!!" OFF)
321option(AKI_ENABLE_CXX_STANDARD_11 "" OFF)
322```
323
3243. napi 注册
325
326* N-API 注册方法:声明模块,利用 napi 接口进行注册
327
328```cpp
329// 初始化导出模块的属性描述符
330EXTERN_C_START
331static napi_value Init(napi_env env, napi_value exports)
332{
333    napi_property_descriptor desc[] = {
334        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr }
335    };
336    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
337    return exports;
338}
339EXTERN_C_END
340
341static napi_module demoModule = {
342    .nm_version = 1,
343    .nm_flags = 0,
344    .nm_filename = nullptr,
345    .nm_register_func = Init,
346    .nm_modname = "entry",
347    .nm_priv = ((void*)0),
348    .reserved = { 0 },
349};
350
351// 注册模块(也可以称之为 c/c++插件)
352extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
353{
354    napi_module_register(&demoModule);
355}
356```
357
358* aki 注册方法:
359
360```cpp
361// Step 1 注册 AKI 插件
362JSBIND_ADDON(hello) // 注册 AKI 插件名: 即为编译*.so名称,规则与NAPI一致
363
364// Step 2 注册 FFI 特性
365JSBIND_GLOBAL() {
366    JSBIND_FUNCTION(SayHello);
367    JSBIND_FUNCTION(FromGlobalJSONStringify);
368}
369```
370
371* JSBIND_ADDON:
372
373```c
374#define JSBIND_ADDON_LAZY(addonName)                                                                               \
375EXTERN_C_START                                                                                                \
376static napi_module _module = {                                                                                \
377    .nm_version =1,                                                                                           \
378    .nm_flags = 0,                                                                                            \
379    .nm_filename = nullptr,                                                                                   \
380    .nm_register_func = aki::JSBind::BindSymbols,                                                             \
381    .nm_modname = #addonName,                                                                                 \
382    .nm_priv = ((void*)0),                                                                                    \
383    .reserved = { 0 },                                                                                        \
384};                                                                                                            \
385extern "C" __attribute__((constructor)) void Register##addonName(void) {                                      \
386    napi_module_register(&_module);                                                                             \
387    AKI_LOG(INFO) << "register AKI addon: " << #addonName;                                                   \
388}    \
389EXTERN_C_END
390
391#define JSBIND_ADDON(addonName)                                          \
392JSBIND_ADDON_LAZY(addonName)
393```
394
395
396
397* JSBIND_GLOBAL:
398
399```c
400// 宏定义,JSBIND_GLOBAL 转 namespace
401#define JSBIND_GLOBAL() namespace
402
403// 宏定义,JSBIND_FUNCTION 转 aki::FunctionDefiner,变量名就是definer+__LINE__,后面执行的是FunctionDefiner构造函数
404#define JSBIND_FUNCTION(__name, ...) aki::FunctionDefiner JSBIND_UNIQUE(definer, __LINE__)(aki::AliasName(#__name, ##__VA_ARGS__), &__name)
405
406// 宏定义,JSBIND_FUNCTION 转 aki::PFunctionDefiner,变量名就是definer+__LINE__,后面执行的是PFunctionDefiner构造函数
407#define JSBIND_PFUNCTION(__name, ...) aki::PFunctionDefiner JSBIND_UNIQUE(definer, __LINE__)(aki::AliasName(#__name, ##__VA_ARGS__), &__name)
408```
409
410
411
412* FunctionDefiner & Init:
413
414```c++
415namespace aki {
416class FunctionDefiner {
417public:
418	......
419	// 注册方法
420    Binding::RegisterFunction(name, Bind#er::AddInvoker(func), &Bind#er::GetInstance());
421};
422}
423```
424
425
426
427* BindSymbols
428
429```c++
430// 对应的就是导出模块里的注册方法: .nm_register_func = aki::JSBind::BindSymbols,
431napi_value aki::JSBind::BindSymbols(napi_env env, napi_value exports)
432{
433    return Init(env, exports);
434}
435
436EXTERN_C_START
437static napi_value Init(napi_env env, napi_value exports) {
438    ......
439    for (auto& function : aki::Binding::GetFunctionList()) {
440        auto akibinder = function.GetBinder();
441        auto wrapper = reinterpret_cast<NapiWrapperFunctionInfo>(akibinder->GetWrapper());
442
443        napi_status status;
444        aki::BindInfo* info = new aki::BindInfo();
445        info->functionNumber = function.GetInvokerId();
446        // 定义function描述符
447        napi_property_descriptor desc = DECLARE_NAPI_FUNCTION(function.GetName(), wrapper, info);
448        // 在导出对象里增加方法属性
449        status = napi_define_properties(env, exports, 1, &desc);
450        AKI_DCHECK(status == napi_ok) << "napi_define_properties failed when binding global function: " << function.GetName();
451        AKI_DLOG(DEBUG) << "binding global function: " << function.GetName();
452    }
453
454    // 下面还有对 Enumeration 和 Class 的注册
455    for (auto& enumeration : aki::Binding::GetEnumerationList()) {
456	    ......
457    }
458
459    for (auto& xlass : aki::Binding::GetClassList()) {
460	    ......
461    }
462
463}
464```
465
466
467
468* 默认注册类 JSBind:提供native直接调用js函数的方法和执行js的线程任务,其特点有:
469
470  * 线程安全:可在非JS线程直接调用。最终会由框架调度JS线程执行业务;
471  * 阻塞式调用:在非JS线程时存在跨线程任务调度。 C++ 会等待 JavaScript 函数执行结束后返回;
472
473  ```c++
474  namespace aki {
475  class AKI_EXPORT JSBind {
476  public:
477  #if JSBIND_USING_NAPI
478      static napi_value BindSymbols(napi_env env, napi_value exports);
479      static napi_value BindSymbols(napi_env env, napi_value exports, std::string moduleName);
480      static napi_value BindSymbols(const char* module);
481      static void SetScopedEnv(napi_env env);
482      static napi_env GetScopedEnv();
483  #endif // JSBIND_USING_NAPI
484
485      static int bindFunction(const std::string& name, JSFunction func);
486      static int unbindFunction(const std::string& name);
487      static void InitTaskRunner(const std::string& name);
488
489  #if JSBIND_SUPPORT_DECLARATION
490      static void Reflect(aki::Callback<void (intptr_t, int32_t)> outputBuildInType,
491                          aki::Callback<void (std::string, std::vector<intptr_t>)> outputFunction);
492      static void QueryType(intptr_t typeId,
493                            aki::Callback<void (int32_t, std::vector<intptr_t>)> outputType);
494  #endif
495      static const JSFunction* GetJSFunction(const std::string& name);
496  private:
497  };
498
499  // | static |
500  int JSBind::bindFunction(const std::string& name, JSFunction func)
501  {
502      return Binding::RegisterJSFunction(name, std::make_unique<JSFunction>(std::move(func)));
503  }
504
505  int JSBind::unbindFunction(const std::string& name)
506  {
507      return Binding::UnRegisterJSFunction(name);
508  }
509
510  void aki::JSBind::InitTaskRunner(const std::string& name) {
511      aki::TaskRunner::Create(name);
512  }
513  }
514  using namespace aki;
515  JSBIND_CLASS(JSBind) {
516      JSBIND_METHOD(bindFunction);
517      JSBIND_METHOD(unbindFunction);
518      JSBIND_METHOD(InitTaskRunner, "initTaskRunner");
519
520  #if JSBIND_SUPPORT_DECLARATION
521      JSBIND_METHOD(Reflect, "reflect");
522      JSBIND_METHOD(QueryType, "queryType");
523  #endif
524  }
525  ```
526
527
528
529
530
531
532### 总结
533
5341. 最新5.0.0 Release 的IDE也能进行开发NativeC++项目,不过要修改工程,参考这个修改:https://forums.openharmony.cn/forum.php?mod=viewthread&tid=3550&page=1#pid86945352. AKI 是一个native应用开发的快速框架,提供了绑定函数,类,枚举给js层使用,以及从native侧获取js全局对象,js方法,js异步任务的方法;给应用开发者提供跨语言的互相访问能力;
5363. AKI 可以和原来的 napi 开发方式并存,混合使用;
537
538
539
540
541
542