OpenHarmony实战开发-如何使用AKI轻松实现跨语言调用。

news2025/2/7 13:51:12

介绍

针对JS与C/C++跨语言访问场景,NAPI使用比较繁琐。而AKI提供了极简语法糖使用方式,一行代码完成JS与C/C++的无障碍跨语言互调,使用方便。本示例将介绍使用AKI编写C++跨线程调用JS函数场景。通过调用C++全局函数,创建子线程来调用JS函数,实现对变量value的加10操作,为开发者使用AKI提供参考。

效果图预览

在这里插入图片描述
使用说明

1.点击页面“AKI跨线程调用JS函数”按钮,每次点击,显示数值加10。

实现思路

以下是使用AKI和NPAI的libuv实现跨线程调用JS函数的实现对比:

  1. AKI和NAPI初始化。
  2. AKI初始化使用JSBIND_ADDON注册Native插件,使用AKI的JSBIND_GLOBAL注册FFI特性,然后在JSBIND_GLOBAL作用域下使用AKI的JSBIND_FUNCTION绑定C++全局函数AkiThreadsCallJs。
...
// 使用JSBIND_ADDON注册Native插件,可从JavaScript import导入插件。注册AKI插件名:即为编译*.so名称,规则与NAPI一致。
JSBIND_ADDON(aki_use_practice)
// 使用JSBIND_GLOBAL注册FFI特性。用于圈定需要绑定的全局函数作用域。
JSBIND_GLOBAL() {
  // 在JSBIND_GLOBAL作用域下使用JSBIND_FUNCTION绑定C++全局函数后,可从JavaScript直接调用。
  JSBIND_FUNCTION(AkiThreadsCallJs);
}
...

NAPI的libuv初始化需要定义napi_property_descriptor结构体,准备模块加载相关信息,将Init函数与模块名等信息记录下来。

...
static napi_value Init(napi_env env, napi_value exports)
{
    // 第一个参数"add"为ArkTS侧对应方法的名称。
    napi_property_descriptor desc[] = {
        {"UvWorkTest", nullptr, UvWorkTest, nullptr, nullptr, nullptr, napi_default, nullptr}
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}

// 准备模块加载相关信息,将Init函数与本模块名等信息记录下来。
static napi_module demoModule = {
  .nm_version = 1,
  .nm_flags = 0,
  .nm_filename = nullptr,
  .nm_register_func = Init,
  .nm_modname = "entry",
  .nm_priv = ((void *)0),
  .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&demoModule); } 

2.AKI和NAPI在native侧的业务函数实现。

AKI在native侧业务函数实现是在AkiThreadsCallJs中创建子线程,子线程中使用aki::JSBind:: GetJSFunction获取指定JavaScript函数akiAccumulate的句柄后,使用Invoke触发调用。

// 定义C++函数AkiThreadsCallJs。从native主线程中创建子线程subThread调用JavaScript函数。
void AkiThreadsCallJs(int value) {
    // 创建子线程subThread
    std::thread subThread([=]() {
        // 使用aki::JSBind::GetJSFunction获取指定JavaScript函数句柄后,使用Invoke触发调用。这里获取JS侧定义的函数akiAccumulate。
        if (auto func = aki::JSBind::GetJSFunction("akiAccumulate")) {
            // 定义一个函数对象callback,该函数对象接受一个整数参数并返回void。
            std::function<void(int)> callback = [](int value) {};
            // 调用JavaScript函数,Invoke<T>指定返回值类型。
            func->Invoke<void>(value, callback);
        }
    });
    // 将子线程subThread从主线程中分离出来,独立运行。
    subThread.detach();
    return;
}

NAPI的libuv在native侧业务函数实现是在native主线程中实现UvWorkTest接口。接口接收到ArkTS传入的JS回调函数后创建子线程,执行函数CallbackUvWorkTest。在CallbackUvWorkTest中创建工作任务workReq,通过uv_queue_work将工作任务添加到libuv队列中,等待被执行。

static napi_value UvWorkTest(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value argv[1] = {0};
    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);

    napi_valuetype valueType = napi_undefined;
    napi_typeof(env, argv[0], &valueType);
    if (valueType != napi_function) {
        OH_LOG_ERROR(LOG_APP, "UvWorkTest param is not function");
        return nullptr;
    }

    OH_LOG_INFO(LOG_APP, "UvWorkTest current value:[%{public}d]", g_cValue);
    for (int i = 0; i < g_threadNum; i++) {
        auto asyncContext = new CallbackContext();
        if (asyncContext == nullptr) {
            OH_LOG_ERROR(LOG_APP, "UvWorkTest new asyncContext fail!");
            return nullptr;
        }
        asyncContext->env = env;
        asyncContext->retData = i;
        OH_LOG_INFO(LOG_APP, "UvWorkTest thread begin index:[%{public}d], value:[%{public}d]", i, g_cValue);
        napi_create_reference(env, argv[0], 1, &asyncContext->callbackRef);
        std::thread testThread(CallbackUvWorkTest, asyncContext);
        testThread.detach();
        OH_LOG_INFO(LOG_APP, "UvWorkTest thread end index:[%{public}d], value:[%{public}d]", i, g_cValue);
    }
    return nullptr;
}

void CallbackUvWorkTest(CallbackContext *context) {
    if (context == nullptr) {
        OH_LOG_ERROR(LOG_APP, "UvWorkTest context is nullptr");
        return;
    }
    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(context->env, &loop);
    // 创建工作数据结构,自定义数据结构添加在data中
    uv_work_t *workReq = new uv_work_t;
    if (workReq == nullptr) {
        if (context != nullptr) {
            napi_delete_reference(context->env, context->callbackRef);
            delete context;
            OH_LOG_INFO(LOG_APP, "UvWorkTest delete context");
            context = nullptr;
        }
        OH_LOG_ERROR(LOG_APP, "UvWorkTest new uv_work_t fail!");
        return;
    }
    workReq->data = (void *)context;
    // 此打印位于子线程
    OH_LOG_INFO(LOG_APP, "UvWorkTest childThread_1 [%{public}d]", g_cValue);
    // 添加工作任务到libuv的队列中
    uv_queue_work(loop, workReq, WorkCallback, AfterWorkCallback);
}

3.AKI和NAPI在ArkTS侧调用JS函数。

AKI在ArkTS侧使用AKI的JSBind.bindFunction绑定JavaScript全局函数akiAccumulate。使用AKI调用C++全局函数AkiThreadsCallJs。

...
// 使用AKI的JSBind.bindFunction绑定JavaScript全局函数。
libaki.JSBind.bindFunction("akiAccumulate", (values: number) => {
  // 对变量value做加10操作,刷新Text组件的value值。
  values += 10;
  this.value = values;
});
// TODO:知识点:使用AKI调用C++全局函数AkiThreadsCallJs,并传入参数value。
libaki.AkiThreadsCallJs(this.value);
...

NAPI的libuv在ArkTS侧调用C++全局函数UvWorkTest。

entry.UvWorkTest((values: number) => {
  values += 10;
  logger.info('UvWorkTest js callback value = ', values.toString());
  this.value = values;
  return values;
}

通过以上AKI和NAPI实现跨线程调用JS的实现步骤的对比,可以发现AKI在native侧相较于NAPI实现的代码量要少很多,使用也更加方便。

高性能知识点

1.AKI使用方便,但相比于NPAI,对性能的损耗相对会多一些。对性能要求不高,且更需要易用性开发的场景,推荐使用AKI。

工程结构&模块类型

akiusepractice                                 // har类型
|---src\main\cpp
|   |---akiusepractice.cpp                     // native层-native侧业务处理
|   |---CMakeLists.txt                         // native层-AKI相关CMake配置
|---src\main\ets\view
|   |---AkiView.ets                            // 视图层-AKI跨线程调用JS函数页面 

模块依赖

本实例依赖AKI。

本实例依赖common模块来实现公共组件FunctionDescription。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→HarmonyOS教学视频:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

HarmonyOS教学视频:语法ArkTS、TypeScript、ArkUI等…视频教程

在这里插入图片描述
在这里插入图片描述

鸿蒙生态应用开发白皮书V2.0PDF: 获取完整版白皮书方式请点击→https://docs.qq.com/doc/DZVVkRGRUd3pHSnFG?u=a42c4946d1514235863bb82a7b2ac128

在这里插入图片描述

鸿蒙 (Harmony OS)开发学习手册→https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

一、入门必看

1.应用开发导读(ArkTS)
2………
在这里插入图片描述

二、HarmonyOS 概念→https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.系统定义
2.技术架构
3.技术特性
4.系统安全
5…

在这里插入图片描述

三、如何快速入门?→https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.基本概念
2.构建第一个ArkTS应用
3…

在这里插入图片描述

四、开发基础知识→https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
在这里插入图片描述

五、基于ArkTS 开发→https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
在这里插入图片描述

更多了解更多鸿蒙开发的相关知识可以参考:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1598366.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++入门之类和对象

C入门之类和对象 文章目录 C入门之类和对象1. 类的6个默认对象2. 构造函数2.1 概念2.2 特性2.3 补丁 3. 析构函数3.1 概念3.2 特性3.3 总结 4. 拷贝构造函数4.1 概念4.2 特性4.3 总结 1. 类的6个默认对象 如果一个类中什么都没有&#xff0c;那么这个类就是一个空类。但是&…

UE5 C++ TimeLine 时间轴练习

一. Actor引入头文件 #include "Components/TimelineComponent.h" 声明CurveFloat 和 TimelineComponent UPROPERTY(EditAnywhere,BlueprintReadWrite,Category "MyCurve")UCurveFloat* MyCurveFloat;UPROPERTY(EditAnywhere, BlueprintReadWrite, Cate…

科技助力上亿用户隐私安全保护,合合信息两款产品再获CCIA PIA星级标识

随着互联网技术的飞速发展&#xff0c;个人信息的收集、存储、使用和传输变得日益频繁&#xff0c;其泄露和滥用的风险也随之增加&#xff0c;个人信息保护已成为社会共同关注的热点议题。近期&#xff0c;“中国网络安全产业联盟&#xff08;CCIA&#xff09;数据安全工作委员…

密码学 | 椭圆曲线数字签名方法 ECDSA(上)

目录 1 ECDSA 是什么&#xff1f; 2 理解基础知识 3 为什么使用 ECDSA&#xff1f; 4 基础数学和二进制 5 哈希 6 ECDSA 方程 7 点加法 8 点乘法 9 陷阱门函数&#xff01; ⚠️ 原文&#xff1a;Understanding How ECDSA Protects Your Data. ⚠️ 写在前面…

OpenHarmony轻量系统开发【9】WiFi之STA模式连接热点

9.1AT指令操作WiFi 我们可以使用AT指令进行Hi3861 WiFi操作&#xff0c;连接热点、ping服务器等。 但是很多时候&#xff0c;我们需要实现开机后自动连接到某个热点&#xff0c;光靠AT指令不行。 Hi3861 为我们提供了WiFi操作的相关API&#xff0c;方便我们编写代码&#xff0…

GitLab 安全漏洞 CVE-2022-1162 如何解决?

本文来自极狐GitLab 官方公众号【极狐GitLab】&#xff0c;原文链接&#xff1a;https://mp.weixin.qq.com/s/JVpA14HHWgt58s3vM5TRcA。 GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab &#xff1a;https…

文字转语音工具:GPT-SoVITS

诸神缄默不语-个人CSDN博文目录 OpenAI官方的TTS模型我在这篇博文中给出了使用教程&#xff1a;ChatGPT 3.5 API的调用不全指南&#xff08;持续更新ing…&#xff09; - 知乎 但是OpenAI的TTS对中文支持不好&#xff0c;有一种老外说中文的美&#xff0c;所以本文介绍另一个…

2024软考中项考哪个版本?应该该如何备考?

2024年1月&#xff0c;备受瞩目的软考中级系统集成项目管理工程师官方教程终于迎来了久违的大改版。为确保广大考生能够有充足的准备时间&#xff0c;软考中项的考试时间被顺延至同年11月&#xff0c;届时&#xff0c;这也将成为软考中项首次依据第3版考纲进行的考试。 新教材核…

数图智慧零售解决方案,赋能零售行业空间资源价值最大化

数图智慧零售解决方案 赋能零售行业空间资源价值最大 在激烈的市场竞争中&#xff0c;如何更好地提升空间资源价值&#xff0c;提高销售额&#xff0c;成为行业关注的焦点。近日&#xff0c;NIQ发布的《2024年中国饮料行业趋势与展望》称&#xff0c;“在传统零售业态店内&…

CSS特效---跳动的文字

1、演示 2、一切尽在代码中 <!--* Author: your name* Date: 2023-10-03 14:42:44* LastEditTime: 2023-10-03 14:56:26* LastEditors: DESKTOP-536UVPC* Description: In User Settings Edit* FilePath: \css-special-effects\跳动的文字.html --> <!DOCTYPE html>…

ARM看门狗定时器

作用 在S3C2440A中&#xff0c;看门狗定时器的作用是当由于噪声和系统错误引起的故障干扰时恢复控制器的工作。 也就是说&#xff0c;系统内部的看门狗定时器需要在指定时间内向一个特殊的寄存器内写入一个数值&#xff0c;俗称喂狗。 如果喂狗的时间过了&#xff0c;那么看门…

行式存储VS列式存储对比

行式存储&#xff1a; 一行代表一个记录的所有字段。 可以快速读取和写入单条记录。 如果要检索一条数据&#xff0c;数据库会读取or写入整条记录&#xff0c;包含所有相关字段。 列式存储&#xff1a; 表中每一列的数据连续存放。这种方式在需要对某一列进行大量运算或分析时…

AI预测福彩3D第37弹【2024年4月16日预测--第8套算法开始计算第5次测试】

今天咱们继续测试第8套算法和模型&#xff0c;今天是第5次测试&#xff0c;目前的测试只是为了记录和验证&#xff0c;不建议大家盲目跟买。。我的目标仍旧是10次命中3-4次!~废话不多说了&#xff0c;直接上结果&#xff01; 2024年4月16日3D的七码预测结果如下 第一套…

数字乡村创新实践探索农业现代化路径:科技赋能农业产业升级、提升乡村治理效能与农民幸福感

随着信息技术的快速发展和数字化时代的到来&#xff0c;数字乡村建设正成为推动农业现代化、提升农业产业竞争力、优化乡村治理以及提高农民幸福感的重要途径。本文将围绕数字乡村创新实践&#xff0c;探讨其在农业现代化路径中的积极作用&#xff0c;以及如何通过科技赋能实现…

C语言中的数据结构- -链表(1)

前言 前几节我们学习了C语言中的数据结构--顺序表&#xff0c;该数据结构类型相较于普通的数组而言有很多的优势&#xff0c;但是它还是在一定层面上存在着一些缺陷&#xff0c;可以归纳为以下三点&#xff1a; 1. 中间/头部的插⼊删除&#xff0c;时间复杂度为O(N)【数组的遍…

每日一VUE——组件基础

文章目录 认识组件如何使用注册方式 组件间传递数据propsprops的验证 组件事件组件事件的验证v-model与自定义事件结合使用 组件插槽动态组件 认识组件 组件由template&#xff0c;script&#xff0c;style三部分组成。 如何使用 定义组件注册组件调用组件 注册方式 全局注…

在STM32中给固定的地址写入一个值,并通过memory窗口进行查看

首先对STM32中存储数据的地方有一个了解 一个是FLASH,一个是RAM RAM是易失存储器&#xff0c;FLASH是非易失存储器&#xff0c;这是最直观的解释。 主要记住以下几点&#xff1a; RAM&#xff08;随机存储器&#xff09;&#xff1a;既可以从中读取数据&#xff0c;也可以写…

面试八股——数据库——分库分表

垂直策略 垂直分库 垂直分表 水平策略 水平分库 水平分表&#xff08;和水平分库差不多&#xff0c;区别是但这些表可以在同一个库内&#xff09;

spring-cloud微服务gateway

核心部分&#xff1a;routes(路由)&#xff0c; predicates(断言)&#xff0c;filters(过滤器) id&#xff1a;可以理解为是这组配置的一个id值&#xff0c;请保证他的唯一的&#xff0c;可以设置为和服务名一致 uri&#xff1a;可以理解为是通过条件匹配之后需要路由到&…

Oauth2.1第三方授权前后端分离实现

前言 Spring Cloud 整合 Spring Security Oauth2 请看我上一篇文章 在当今的数字化时代&#xff0c;随着微服务架构的流行和前后端分离技术的广泛应用&#xff0c;如何实现安全、高效的用户认证与授权成为了开发者们面临的重要挑战。Spring Cloud与Spring Security OAuth2作为J…