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

3 回复

《NAPI导出C++类、模块、异步方法、及对象参数》

nice.

更多关于HarmonyOS鸿蒙Next中NAPI导出C++类、模块、异步方法及对象参数的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,NAPI(Native API)允许开发者导出C++类、模块、异步方法及对象参数。以下是相关内容的简要说明:

导出C++类

通过NAPI,可以将C++类暴露给JavaScript。使用napi_define_class函数定义类,并通过napi_property_descriptor设置类的属性和方法。

导出模块

使用napi_module_register函数注册模块,模块中可以包含多个C++类或函数。模块导出后,可以在JavaScript中通过require引入。

异步方法

NAPI支持异步操作,通过napi_create_async_work创建异步工作对象,并在工作完成后调用回调函数。异步方法可以避免阻塞主线程,提升应用性能。

对象参数

在NAPI中,可以通过napi_get_propertynapi_set_property等函数操作JavaScript对象。C++函数可以接收JavaScript对象作为参数,并对其进行处理。

这些功能使得在HarmonyOS中,C++代码能够与JavaScript进行高效交互,扩展了应用的能力。

在HarmonyOS鸿蒙Next中,NAPI(Native API)允许开发者将C++类、模块、异步方法及对象参数导出到JavaScript环境中。首先,使用napi_define_class定义C++类,并通过napi_create_object创建模块对象。异步方法可通过napi_create_async_work实现,确保非阻塞执行。对象参数的传递则通过napi_get_cb_infonapi_set_named_property处理,确保数据在C++和JavaScript间的无缝交互。

回到顶部