HarmonyOS鸿蒙Next中NAPI导出C++类、模块、异步方法及对象参数
HarmonyOS鸿蒙Next中NAPI导出C++类、模块、异步方法及对象参数
一:接口介绍
因工作需要,学习了下NAPI导出一个类、导出一个功能模块、异步方法以及复杂参数的传递。这些资料较少,所以整理了下我使用的方法供大家参考。以下内容基于API11编写,水平有限,如遇到错误的或更优的方法,请大家帮忙指出。
先看一下ArkTS的接口定义:
// index.d.ts
declare namespace classNapi {
const square: (a: number) => number;
class MyDemo {
constructor(name:string)
name: string
add(a: number, b: number): number
sub(a: number, b: number): number
}
class HisDemo {
constructor(name:string)
name: string
mul(a: number, b: number): number
div(a: number, b: number): number
}
namespace callback{
const getDataCallBack:(a:string,callback:(a:string)=>void)=>void;
const getDataPromise:(a:string)=>Promise<string>;
const startThread:(callback:(progress:number)=>void)=>void;
}
namespace objectparam{
interface param{
str:string,
int:number,
bool:boolean,
func:(str:string,int:number,bool:boolean)=>void
}
const initParam:(obj:param)=>void;
const callParamFunc:()=>void;
}
}
export default classNapi;
square为一个单独的方法 MyDemo/HisDemo是两个C++类 callback模块,实现了三个异步方法,callback/promise/thread objectparam用于模块导出的学习以及obj参数的解析 下面将分别讲一下这些内容的具体实现
二、实现方法
1、模块注册
// napi_init.cpp
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
// 导出C++类
RegistHisDemo(env,exports);
// 导出C++类
RegistMyDemo(env, exports);
// 导出单个方法
RegistFunction(env,exports);
// 导出异步方法
RegistAsync(env,exports);
// 导出模块
RegistObjectParam(env,exports);
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "class",
.nm_priv = ((void *)0),
.reserved = {0},
};
extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
所有模块、类、方法的注册都统一在这里实现,注意nm_modname需要和工程中新建的模块名一样,Init完成各个模块的注册。
2、模块导出的学习
objectParam.cpp
static napi_value RegistParam(napi_env env, napi_value exports) {
// 给对象设置方法
napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("initParam", InitParam),
DECLARE_NAPI_FUNCTION("callParamFunc", CallParamFunc),
};
// 批量绑定方法
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
return exports;
}
napi_value RegistObjectParam(napi_env env, napi_value exports) {
napi_value objectparam = nullptr;
napi_status rslt_status = napi_cancelled;
// 创建一个对象,用于封装模块
NAPI_CALL(env, napi_create_object(env, &objectparam));
// 为该对象设置函数及属性
if (nullptr == RegistParam(env, objectparam)) {
napi_throw_error(env, nullptr, "RegistCallback error");
return nullptr;
}
// 外导出的对象 objectparam
napi_property_descriptor desc[] = {
DECLARE_NAPI_PROPERTY("objectparam",objectparam)
};
// 批量的向给定 Object 中定义属性
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
return exports;
}
先创建一个对象,用于封装模块(napi_create_object(env, &objectparam)); 为这个对象设置绑定方法; 导出对象;
2.1 解析复杂的参数(对象)
参数内容是这样的 { str:‘param’, int:123, bool:true, func:this.callback_func } 三个基本参数以及一个回调函数 步骤如下:
2.1.1获取对象参数napi_get_cb_info
2.1.2通过参数名获取参数napi_get_named_property
参数名就是"str"、“int”、“bool”、“func”
根据参数类型,创建不同的C++参数
其中回调函数需要着重注意下,通过名字获取到参数后napi_get_named_property,需要通过这个参数创建回调的引用napi_create_reference,napi_ref call_back;
struct _ObjectParam{
std::string str;
int data;
bool isUse;
napi_ref call_back;
};
static _ObjectParam gObjectParam;
static napi_value InitParam(napi_env env,napi_callback_info info){
char *pStr = nullptr;
size_t str_len = 0;
size_t argc = 1;
napi_value args[1];
napi_valuetype object_type = napi_undefined;
napi_value value_str = nullptr;
napi_value value_int = nullptr;
napi_value value_bool = nullptr;
napi_value value_func = nullptr;
napi_valuetype valuetype_str = napi_undefined;
napi_valuetype valuetype_func = napi_undefined;
napi_valuetype valuetype_int = napi_undefined;
napi_valuetype valuetype_bool = napi_undefined;
napi_value rslt_value = nullptr;
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
OH_LOG_Print(LogType::LOG_APP, LogLevel::LOG_INFO, 0, "ckTest", "InitParam data %{public}d", 1);
if(1 > argc){
napi_throw_error((env), nullptr, "get param cnt err");
return nullptr;
}
NAPI_CALL(env,napi_typeof(env, args[0], &object_type));
if(napi_object != object_type){
napi_throw_error((env), nullptr, "param type is not object");
return nullptr;
}
OH_LOG_Print(LogType::LOG_APP, LogLevel::LOG_INFO, 0, "ckTest", "InitParam data %{public}d", 2);
NAPI_CALL(env, napi_get_named_property(env, args[0], "str", &value_str));
NAPI_CALL(env, napi_typeof(env, value_str, &valuetype_str));
if(napi_string != valuetype_str){
napi_throw_error((env), nullptr, "param type is not string");
return nullptr;
}
NAPI_CALL(env, napi_get_value_string_utf8(env, value_str, nullptr, 0, &str_len));
pStr = new char[str_len+1];
if(nullptr == pStr){
napi_throw_error((env), nullptr, "get str param buffer fail");
return nullptr;
}
if(napi_ok != napi_get_value_string_utf8(env, value_str, pStr, str_len+1, &str_len)){
napi_throw_error((env), nullptr, "get str param fail");
delete [] pStr;
return nullptr;
}
gObjectParam.str = pStr;
delete[] pStr;
pStr = nullptr;
OH_LOG_Print(LogType::LOG_APP, LogLevel::LOG_INFO, 0, "ckTest", "InitParam data %{public}d", 3);
NAPI_CALL(env, napi_get_named_property(env, args[0], "int", &value_int));
NAPI_CALL(env, napi_typeof(env, value_int, &valuetype_int));
if (napi_number != valuetype_int) {
napi_throw_error((env), nullptr, "param type is not int");
return nullptr;
}
int32_t int_result = 0;
NAPI_CALL(env, napi_get_value_int32(env, value_int, &int_result));
gObjectParam.data = int_result;
OH_LOG_Print(LogType::LOG_APP, LogLevel::LOG_INFO, 0, "ckTest", "InitParam data %{public}d", 4);
NAPI_CALL(env, napi_get_named_property(env, args[0], "bool", &value_bool));
NAPI_CALL(env, napi_typeof(env, value_bool, &valuetype_bool));
if (napi_boolean != valuetype_bool) {
napi_throw_error((env), nullptr, "param type is not string");
return nullptr;
}
bool bool_result = false;
NAPI_CALL(env, napi_get_value_bool(env, value_bool, &bool_result));
gObjectParam.isUse = bool_result;
OH_LOG_Print(LogType::LOG_APP, LogLevel::LOG_INFO, 0, "ckTest", "InitParam data %{public}d", 5);
NAPI_CALL(env, napi_get_named_property(env, args[0], "func", &value_func));
NAPI_CALL(env, napi_typeof(env, value_func, &valuetype_func));
if (napi_function != valuetype_func) {
napi_throw_error((env), nullptr, "param type is not func");
return nullptr;
}
NAPI_CALL(env, napi_create_reference(env, value_func, 1, &gObjectParam.call_back));
OH_LOG_Print(LogType::LOG_APP, LogLevel::LOG_INFO, 0, "ckTest", "InitParam data %{public}d", 6);
NAPI_CALL(env,napi_get_undefined(env, &rslt_value));
return rslt_value;
}
2.2回调函数的执行
拿到回调函数的引用napi_ref后,在执行回调函数的前,需要通过napi_ref获取到回调函数napi_get_reference_value,然后再执行回调函数napi_call_function。
// ObjectParam.cpp
static napi_value CallParamFunc(napi_env env, napi_callback_info info) {
napi_value rslt_value = nullptr;
napi_value callback = nullptr;
size_t argc = 3;
napi_value args[3] = {nullptr};
napi_value value_str = nullptr;
napi_value value_bool = nullptr;
napi_value value_int = nullptr;
napi_value tmp_result = nullptr;
OH_LOG_Print(LogType::LOG_APP, LogLevel::LOG_INFO, 0, "ckTest", "CallParamFunc str %{public}s", gObjectParam.str.c_str());
OH_LOG_Print(LogType::LOG_APP, LogLevel::LOG_INFO, 0, "ckTest", "CallParamFunc data %{public}d", gObjectParam.data);
OH_LOG_Print(LogType::LOG_APP, LogLevel::LOG_INFO, 0, "ckTest", "CallParamFunc isUse %{public}d", gObjectParam.isUse);
NAPI_CALL(env, napi_create_string_utf8(env, gObjectParam.str.c_str(), gObjectParam.str.length(), &value_str));
NAPI_CALL(env,napi_create_int32(env, gObjectParam.data, &value_int));
NAPI_CALL(env,napi_get_boolean(env,gObjectParam.isUse,&value_bool));
args[0] = value_str;
args[1] = value_int;
args[2] = value_bool;
NAPI_CALL(env, napi_get_reference_value(env, gObjectParam.call_back, &callback));
NAPI_CALL(env,napi_call_function(env, nullptr, callback, argc, args, &tmp_result));
NAPI_CALL(env, napi_get_undefined(env, &rslt_value));
return rslt_value;
}
以上就是回调函数的执行
2.3 总结
模块导出需要建立一个对象作为模块的载体,往对象里面加入方法或者属性,最后再将对象导出即可;复杂参数可作为一个对象传入,拿到对象后,通过属性名获得参数值,记得参数值类型的判断;回调函数参数需要创建一个napi_ref的引用,执行回调函数的时候,需要根据回调函数的引用获取到回调函数,然后再执行回调函数。ArkTs端调用如下:
// index.ets
let obj_param: libclass.objectparam.param = {
str:'param',
int:123,
bool:true,
func:this.callback_func
};
libclass.objectparam.initParam(obj_param);
libclass.objectparam.callParamFunc();
3、异步回调、promise、thread
异步回调需要加入异步队列完成,thread需要napi创建线程完成,配合libuv库一起使用
3.1 先完成模块的注册
// async.cpp
static napi_value RegistCallback(napi_env env, napi_value exports){
napi_status rslt_status = napi_invalid_arg;
// 定义方法的描述
napi_property_descriptor desc[] = {
{"getDataCallBack", nullptr, GetData, nullptr, nullptr, nullptr, napi_default, nullptr},
{"getDataPromise", nullptr, GetData, nullptr, nullptr, nullptr, napi_default, nullptr},
{"startThread", nullptr, StartThread, nullptr, nullptr, nullptr, napi_default, nullptr}
};
// 设置描述的方法绑定到对象
rslt_status = napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
if(napi_ok != rslt_status){
return nullptr;
}
return exports;
}
napi_value RegistAsync(napi_env env, napi_value exports) {
napi_value callBack = nullptr;
napi_status rslt_status = napi_cancelled;
// 创建一个异步模块对象
rslt_status = napi_create_object(env, &callBack);
if(napi_ok != rslt_status){
napi_throw_error(env, "-1", "napi_create_object error");
return nullptr;
}
// 为模块设置方法或属性
if(nullptr == RegistCallback(env,callBack)){
napi_throw_error(env, "-1", "RegistCallback error");
return nullptr;
}
// 定义模块的描述
napi_property_descriptor desc[] = {
{"callback", nullptr, nullptr, nullptr, nullptr, callBack, napi_default, nullptr},
};
// 导出模块
rslt_status = napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
if (napi_ok != rslt_status) {
napi_throw_error(env, "-1", "napi_define_properties callBack error");
return nullptr;
}
return exports;
}
模块的导出和例子二一样,不再赘述
3.2 异步方法的实现
// async.cpp
struct CallBackContext{
napi_async_work work;
napi_ref call_back;
napi_deferred promise;
std::string data;
std::string result;
};
/**
* 执行异步逻辑
* @param env
* @param data
*/
static void on_async_execute_callback(napi_env env, void *data) {
CallBackContext* callBackContext = (CallBackContext*)data;
callBackContext->result = callBackContext->data.append("on_async_execute_callback");
return;
}
/**
* 异步完成函数
* @param env
* @param status
* @param data
*/
static void on_async_complete_callback(napi_env env, napi_status status, void *data) {
CallBackContext *callBackContext = (CallBackContext *)data;
napi_value returnvalue = nullptr;
napi_status rslt_status = napi_invalid_arg;
do{
// 创建结果字符串
rslt_status = napi_create_string_utf8(env, callBackContext->result.c_str(), callBackContext->result.length(),
&returnvalue);
if(napi_ok != rslt_status){
napi_throw_error(env, "-1", "on_async_complete_callback napi_create_string_utf8 error");
break;
}
// 处理异步回调
if(callBackContext->call_back){
napi_value call_back;
// 获取异步引用
rslt_status = napi_get_reference_value(env, callBackContext->call_back, &call_back);
if (napi_ok != rslt_status) {
napi_throw_error(env, "-1", "on_async_complete_callback napi_get_reference_value error");
break;
}
napi_value tmp_value = nullptr;
// 调用异步接口
rslt_status = napi_call_function(env, nullptr, call_back, 1, &returnvalue, &tmp_value);
if (napi_ok != rslt_status) {
napi_throw_error(env, "-1", "on_async_complete_callback napi_call_function error");
break;
}
}else{ // 处理promise
// 调用promise的resolve状态方法
rslt_status = napi_resolve_deferred(env, callBackContext->promise, returnvalue);
if (napi_ok != rslt_status) {
napi_throw_error(env, "-1", "on_async_complete_callback napi_resolve_deferred error");
break;
}
}
}while(0);
// 从异步队列中移除该异步任务
napi_delete_async_work(env, callBackContext->work);
// 删除异步回调引用
if(callBackContext->call_back){
napi_delete_reference(env, callBackContext->call_back);
}
delete callBackContext;
callBackContext = nullptr;
}
static napi_value GetData(napi_env env, napi_callback_info info) {
size_t argc = 0;
size_t data_len = 0;
char *pData = nullptr;
napi_value args[2] = {nullptr};
napi_status rslt_status = napi_invalid_arg;
napi_valuetype args_string = napi_undefined;
napi_valuetype args_callback = napi_undefined;
napi_value returnValue = nullptr;
// 获取参数个数
rslt_status = napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);
if(napi_ok != rslt_status){
napi_throw_error(env, "-1", "GetData napi_get_cb_info error");
return nullptr;
}
//获取参数
rslt_status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (napi_ok != rslt_status) {
napi_throw_error(env, "-1", "GetData napi_get_cb_info error");
return nullptr;
}
// 判断第一个参数类型
napi_typeof(env, args[0], &args_string);
if(napi_string != args_string){
napi_throw_error(env, "-1", "GetData args_string type error");
return nullptr;
}
// 判断第二个参数类型
if(1 < argc){
napi_typeof(env, args[1], &args_callback);
if (napi_function != args_callback) {
napi_throw_error(env, "-1", "GetData args_callback type error");
return nullptr;
}
}
// 创建一个异步方法需要用到的结构体
CallBackContext* callBackContext = new CallBackContext();
// 获取第一个字符串的长度
napi_get_value_string_utf8(env, args[0], nullptr, 0, &data_len);
if(0 > data_len){
napi_throw_error(env, "-1", "GetData napi_get_value_string_utf8 get len error");
return nullptr;
}
pData = new char[data_len+1];
if(nullptr == pData){
napi_throw_error(env, "-1", "GetData new char error");
return nullptr;
}
do {
// 获取第一个字符串
rslt_status = napi_get_value_string_utf8(env, args[0], pData, data_len + 1, &data_len);
if (napi_ok != rslt_status) {
napi_throw_error(env, "-1", "GetData napi_get_value_string_utf8 get data error");
break;
}
// 保存入参值
callBackContext->data = pData;
if (1 < argc) {
// 创建一个异步引用
rslt_status = napi_create_reference(env, args[1], 1, &callBackContext->call_back);
if(napi_ok != rslt_status){
napi_throw_error(env, "-1", "GetData napi_create_reference get callback error");
break;
}
napi_get_undefined(env, &returnValue);
}else{
// 创建一个promise引用
rslt_status = napi_create_promise(env, &callBackContext->promise, &returnValue);
if (napi_ok != rslt_status) {
napi_throw_error(env, "-1", "GetData napi_create_promise get promise error");
break;
}
}
// 设置一个异步工作别名
napi_value resource_name = nullptr;
napi_create_string_utf8(env, "GetData", NAPI_AUTO_LENGTH, &resource_name);
// 创建一个异步工作 on_async_execute_callback异步处理函数 on_async_complete_callback异步完成函数
rslt_status = napi_create_async_work(env, nullptr, resource_name, on_async_execute_callback, on_async_complete_callback, (void *)callBackContext,
&callBackContext->work);
if(napi_ok != rslt_status){
napi_throw_error(env, "-1", "GetData napi_create_async_work get async error");
break;
}
// 添加到异步队列
rslt_status = napi_queue_async_work(env, callBackContext->work);
if (napi_ok != rslt_status) {
napi_throw_error(env, "-1", "GetData napi_queue_async_work get async error");
break;
}
}
while (0);
if(pData){
delete[] pData;
pData = nullptr;
}
return returnValue;
}
先解析参数。上述例子中,第一个入参是待处理的字符串,如果有第二个入参,那么第二个入参就是回调函数,如果没有第二个入参,那么该异步接口返回一个promise,从promise中获取异步处理值。
3.2.1 获取异步回调参数如第二章节所属,不再赘述
3.2.2 Promise回调
通过napi_create_promise创建一个napi_deferred,异步处理结束后,通过napi_deferred的resovled、reject方法放回执行结果
3.2.3 创建异步任务
获取参数后,通过napi_create_async_work创建异步任务,并通过napi_queue_async_work把异步任务加到异步队列中
3.2.4 执行异步任务
3.2.5 返回异步结果
callback的异步调用和上一个章节一样,不再赘述;promise的结果返回需要通过函数napi_resolve_deferred返回正确结果,通过napi_reject_deferred返回异常结果
3.3 thread
// async.cpp
struct ThreadContext {
napi_env env;
napi_ref call_back;
int progress;
};
static void on_work_cb(uv_work_t *req) {
ThreadContext *threadContext = reinterpret_cast<ThreadContext *>(req->data);
// 异步执行逻辑
while (threadContext && threadContext->progress < 100) {
threadContext->progress += 1;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
static void on_after_work_cb(uv_work_t *req, int status) {
ThreadContext *threadContext = reinterpret_cast<ThreadContext *>(req->data);
napi_handle_scope scope = nullptr;
napi_value call_back = nullptr,ret_value = nullptr,tmp_value = nullptr;
napi_status rslt_status = napi_invalid_arg;
// 打开handle scope用于管理napi_value的生命周期,否则会内存泄露。
napi_open_handle_scope(threadContext->env, &scope);
do{
if (nullptr == scope) {
napi_throw_error(threadContext->env, "-1", "on_after_work_cb napi_open_handle_scope error");
break;
}
// 获取ArkTs的回调引用
rslt_status = napi_get_reference_value(threadContext->env, threadContext->call_back, &call_back);
if (napi_ok != rslt_status || nullptr == call_back) {
napi_throw_error(threadContext->env, "-1", "on_after_work_cb napi_get_reference_value error");
break;
}
rslt_status = napi_create_int32(threadContext->env, threadContext->progress, &ret_value);
if (napi_ok != rslt_status || nullptr == ret_value) {
napi_throw_error(threadContext->env, "-1", "on_after_work_cb napi_create_int32 error");
break;
}
// 执行回调函数
napi_call_function(threadContext->env, nullptr, call_back, 1, &ret_value, &tmp_value);
}while(0);
napi_close_handle_scope(threadContext->env, scope);
if (threadContext) {
delete threadContext;
threadContext = nullptr;
}
if(req){
delete req;
req = nullptr;
}
static void downloadTask(ThreadContext *threadContext){
napi_status rslt_status = napi_invalid_arg;
uv_work_t *work = new uv_work_t;
do{
// 从env中获取ArkTs线程的loop
uv_loop_s *loop = nullptr;
rslt_status = napi_get_uv_event_loop(threadContext->env, &loop);
if (napi_ok != rslt_status) {
napi_throw_error(threadContext->env, "-1", "downloadTask napi_get_uv_event_loop error");
break;
}
if(nullptr == work){
napi_throw_error(threadContext->env, "-1", "downloadTask new uv_work_t error");
break;
}
work->data = threadContext;
// uv_queue_work(loop, work, uv_work_cb work_cb, uv_after_work_cb after_work_cb)
// 将一个任务提交到 libuv 的工作队列中 on_work_cb执行异步任务 on_after_work_cb异步任务执行完成
uv_queue_work(loop, work, on_work_cb, on_after_work_cb);
}while(0);
}
static napi_value StartThread(napi_env env, napi_callback_info info){
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_value return_value = nullptr;
napi_status rslt_status = napi_invalid_arg;
napi_valuetype callback_type = napi_undefined;
napi_ref callback = nullptr;
do{
// 获取参数
rslt_status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (napi_ok != rslt_status) {
napi_throw_error(env, "-1", "StartThread napi_get_cb_info error");
break;
}
// 获取参数类型
rslt_status = napi_typeof(env, args[0], &callback_type);
if (napi_ok != rslt_status) {
napi_throw_error(env, "-1", "StartThread napi_typeof error");
break;
}
// 创建一个异步应用
rslt_status = napi_create_reference(env, args[0], 1, &callback);
if (napi_ok != rslt_status) {
napi_throw_error(env, "-1", "StartThread napi_create_reference error");
break;
}
// 创建线程上下文
ThreadContext* threadContext = new ThreadContext();
if(nullptr == threadContext){
napi_throw_error(env, "-1", "StartThread new ThreadContext() error");
break;
}
threadContext->env = env;
threadContext->call_back = callback;
// 创建一个download线程
std::thread downloadThread(downloadTask, threadContext);
// 分离线程
downloadThread.detach();
}while(0);
return nullptr;
}
代码中注释很清楚,参考代码自行学习;注意,还是需要一个线程上下文struct ThreadContext 来保存线程运行环境
3.4 总结
异步的执行分三种,callback/promise/thread callback和promise是加入到异步队列中等待执行,执行结束后通过回调或promise的方式把数据返回;线程则需要获取一个ArkTs的loop循环,将线程加入到loop循环中执行,或者可以使用napi创建一个安全的线程运行
// index.ets
libclass.callback.getDataCallBack("callback",(a:string)=>{
hilog.info(0x0000, 'ckTest', 'Test call back : %{public}s', a);
})
libclass.callback.getDataPromise("promise")
.then(
(value:string)=>{
hilog.info(0x0000, 'ckTest', 'Test promise : %{public}s', value);
},(reason:Object)=>{
hilog.info(0x0000, 'ckTest', 'Test promise rejecet : %{public}s', JSON.stringify(reason));
})
libclass.callback.startThread((progress:number)=>{
hilog.info(0x0000, 'ckTest', 'Test startThread progress : %{public}d', progress);
})
4、C++类
创建并实现一个C++类;讲方法绑定到对象上,导出对象
4.1 代码如下
// Hisdemo.cpp
// js对象sub函数
static napi_value JsMul(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_value jDemo = nullptr;
napi_get_cb_info(env, info, &argc, args, &jDemo, nullptr);
HisDemo *cDemo = nullptr;
// 将js对象转为c对象
napi_unwrap(env, jDemo, (void **)&cDemo);
// 获取js传递的参数
int value0;
napi_get_value_int32(env, args[0], &value0);
int value1;
napi_get_value_int32(env, args[1], &value1);
int cResult = cDemo->mul(value0, value1);
napi_value jResult;
napi_create_int32(env, cResult, &jResult);
return jResult;
}
// js对象sub函数
static napi_value JsDiv(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_value jDemo = nullptr;
napi_get_cb_info(env, info, &argc, args, &jDemo, nullptr);
HisDemo *cDemo = nullptr;
// 将js对象转为c对象
napi_unwrap(env, jDemo, (void **)&cDemo);
// 获取js传递的参数
int value0;
napi_get_value_int32(env, args[0], &value0);
int value1;
napi_get_value_int32(env, args[1], &value1);
int cResult = cDemo->div(value0, value1);
napi_value jResult;
napi_create_int32(env, cResult, &jResult);
return jResult;
}
// js对象构造函数
static napi_value JsHisDemoConstructor(napi_env env, napi_callback_info info) {
// 创建napi对象
napi_value jDemo = nullptr;
size_t argc = 1;
napi_value args[1] = {0};
// 获取构造函数入参
napi_get_cb_info(env, info, &argc, args, &jDemo, nullptr);
// args[0] js传入的参数
char name[50];
size_t result = 0;
napi_get_value_string_utf8(env, args[0], name, sizeof(name) + 1, &result);
// 创建C++对象
HisDemo *cDemo = new HisDemo(name);
OH_LOG_INFO(LOG_APP, "%{public}s", (cDemo->name).c_str());
// 设置js对象name属性
napi_set_named_property(env, jDemo, "name", args[0]);
// 绑定JS对象与C++对象
napi_wrap(
env, jDemo, cDemo,
// 定义js对象回收时回调函数,用来销毁C++对象,防止内存泄漏
[](napi_env env, void *finalize_data, void *finalize_hint) {
HisDemo *cDemo = (HisDemo *)finalize_data;
delete cDemo;
cDemo = nullptr;
},
nullptr, nullptr);
return jDemo;
}
HisDemo::HisDemo(std::string name) { this->name = name; }
HisDemo::HisDemo() { OH_LOG_INFO(LOG_APP,"HisDemo"); }
HisDemo::~HisDemo() { OH_LOG_INFO(LOG_APP,"~HisDemo"); }
int HisDemo::mul(int a, int b) { return a * b; }
int HisDemo::div(int a, int b) { return a / b; }
napi_value RegistHisDemo(napi_env env, napi_value exports){
napi_property_descriptor classHisDemoProp[] = {
{"mul", nullptr, JsMul, nullptr, nullptr, nullptr, napi_default, nullptr},
{"div", nullptr, JsDiv, nullptr, nullptr, nullptr, napi_default, nullptr}};
napi_value hisDemo = nullptr;
const char *hisDemoName = "HisDemo";
// 建立JS构造函数与C++方法的关联,指定2个property
napi_define_class(env, hisDemoName, sizeof(hisDemoName), JsHisDemoConstructor, nullptr,
sizeof(classHisDemoProp) / sizeof(classHisDemoProp[0]), classHisDemoProp, &hisDemo);
// 为class对象设置别名
napi_set_named_property(env, exports, hisDemoName, hisDemo);
return exports;
}
4.2 创建一个类HisDemo
4.3 用napi_define_class为类绑定JS方法,然后使用napi_set_named_property将类设置一个别名并导出
4.4 使用的时候,通过napi_get_cb_info(env, info, &argc, args, &jDemo, nullptr);获取JS对象,并将jDemo转换成C++对象,后续就可以调用c++的方法和属性。
4.5 napi_define_class绑定方法的时候,不要忘记构造函数和析构函数的定义
ArkTs的使用方法
// index.ets
let hisDemo:libclass.HisDemo = new libclass.HisDemo('test1');
ilog.info(0x0000, 'ckTest', 'Test hisDemo = %{public}s',JSON.stringify(hisDemo));
hilog.info(0x0000, 'ckTest', 'Test NAPI 2 * 3 = %{public}d',hisDemo.mul(2,3));
hilog.info(0x0000, 'ckTest', 'Test NAPI 4 / 2 = %{public}d',hisDemo.div(4,2));
5 总结
实现了同一个module中导出单个函数、模块、类、异步方法、线程。
更多关于HarmonyOS鸿蒙Next中NAPI导出C++类、模块、异步方法及对象参数的实战教程也可以访问 https://www.itying.com/category-93-b0.html
《NAPI导出C++类、模块、异步方法、及对象参数》
nice.
更多关于HarmonyOS鸿蒙Next中NAPI导出C++类、模块、异步方法及对象参数的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中,NAPI(Native API)允许开发者将C++类、模块、异步方法及对象参数导出到JavaScript环境中。首先,使用napi_define_class
定义C++类,并通过napi_create_object
创建模块对象。异步方法可通过napi_create_async_work
实现,确保非阻塞执行。对象参数的传递则通过napi_get_cb_info
和napi_set_named_property
处理,确保数据在C++和JavaScript间的无缝交互。