首页 > 其他分享 >Openharmony应用NAPI详解--进阶篇1

Openharmony应用NAPI详解--进阶篇1

时间:2023-04-23 11:37:37浏览次数:49  
标签:status Openharmony callback -- work 进阶篇 env async napi

NAPI面向C++的异步接口

3.C++实现NAPI异步接口需要做到三步

  • 同步返回结果给js/ets调用者
  • 另起线程完成异步操作
  • 通过回调(callback)或Promise将异步操作结果返回

4.异步接口

// foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
...
    DECLARE_NAPI_FUNCTION("sendFile", JsSendFile),
...

根据映射,同步接口sendFile对应C++的实现是JsSendFile。

  • 函数声明
// foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
napi_value JsSendFile(napi_env env, napi_callback_info info)
{
......
}
  • 获取入参

同样是获取参数个数与参数,根据d.ts里sendFile接口定义,第五个参数为callback或没有参数,callback接口返回一个void, promise接口返回一个promise对象。

// foundation/filemanagement/dfs_service
declare namespace SendFile {
  function sendFile(deviceId: string, sourPath: Array<string>, destPath: Array<string>, fileCount: number, callback: AsyncCallback<number>);
  function sendFile(deviceId: string, sourPath: Array<string>, destPath: Array<string>, fileCount: number): Promise<number>;
}

此时获取参数个数与参数类型与具体数据,被一个类NFuncArg对象funcArg进行解析。

// foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
napi_value JsSendFile(napi_env env, napi_callback_info info)
{
	NFuncArg funcArg(env, info);
    if (!funcArg.InitArgs(DFS_ARG_CNT::FOUR, DFS_ARG_CNT::FIVE)) {
        NError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
        return nullptr;
    }
...
}

我们熟悉的获取参数的方法napi_get_cb_info在类NFuncArg中可以找到。

bool NFuncArg::InitArgs(std::function<bool()> argcChecker)
{
    SetArgc(0);
    argv_.reset();

    size_t argc;
    napi_value thisVar;
    napi_status status = napi_get_cb_info(env_, info_, &argc, nullptr, &thisVar, nullptr);
    if (status != napi_ok) {
        HILOGE("Cannot get num of func args for %{public}d", status);
        return false;
    }
    if (argc) {
        argv_ = make_unique<napi_value[]>(argc);
        status = napi_get_cb_info(env_, info_, &argc, argv_.get(), &thisVar, nullptr);
        if (status != napi_ok) {
            HILOGE("Cannot get func args for %{public}d", status);
            return false;
        }
    }
    SetArgc(argc);
    SetThisVar(thisVar);

    return argcChecker();
}

略过参数的类型转化与参数值校验,直接进入callback与promise的判断。

napi_value JsSendFile(napi_env env, napi_callback_info info)
{
...
	if (funcArg.GetArgc() == DFS_ARG_CNT::FOUR) {
        return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbComplete).val_;
    } else if (funcArg.GetArgc() == DFS_ARG_CNT::FIVE) {
        NVal cb(env, funcArg[DFS_ARG_POS::FIFTH]);// cb为传入的回调函数
        return NAsyncWorkCallback(env, thisVar, cb).Schedule(procedureName, cbExec, cbComplete).val_;
    }
}

此时的判断的方式通过传入的参数个数是4还是5来判断callback与promise。

如果有参数个数相同的情况时,可以根据参数的类型来进行判断。

// 可以改写成如下判断
napi_value JsSendFile(napi_env env, napi_callback_info info)
{
...
	napi_valuetype valueType = napi_undefined;
    // (此处假设promise接口也有第5个参数,否则会报异常错误) 
	napi_typeof(env, funcArg[DFS_ARG_POS::FIFTH], &valueType);
    if (valueType == napi_function) { // Js调用的是callback接口
        ...
    } else {//Js调用的是promise接口
        ...
    }
...
}

此时,异步的方式需要另起线程,将参数传入线程后,JsSendFile直接返回。

NAPI框架中,异步的线程实现基于napi_create_async_work函数创建异步工作项。

代码中通过两个封装类NAsyncWorkPromise/NAsyncWorkCallback的Schedule来实现。可以在两个类的Schedule接口找到napi_create_async_work方法调用。

// foundation/filemanagement/file_api/interfaces/kits/js/src/common/napi/n_async/n_async_work_callback.cpp
NVal NAsyncWorkCallback::Schedule(string procedureName, NContextCBExec cbExec, NContextCBComplete cbComplete)
{
...
    napi_status status =
        napi_create_async_work(env_, nullptr, resource, CallbackExecute, CallbackComplete, ctx_, &ctx_->awork_);
    if (status != napi_ok) {
        HILOGE("INNER BUG. Failed to create async work for %{public}d", status);
        return NVal();
    }
...
}

// foundation/filemanagement/file_api/interfaces/kits/js/src/common/napi/n_async/n_async_work_promise.cpp
NVal NAsyncWorkPromise::Schedule(string procedureName, NContextCBExec cbExec, NContextCBComplete cbComplete)
{
...
    napi_value resource = NVal::CreateUTF8String(env_, procedureName).val_;
    status = napi_create_async_work(env_, nullptr, resource, PromiseOnExec, PromiseOnComplete, ctx_, &ctx_->awork_);
    if (status != napi_ok) {
        HILOGE("INNER BUG. Failed to create async work for %{public}d", status);
        return NVal();
    }
...
}

首先先分析下napi_create_async_work的方法定义

napi_status napi_create_async_work(napi_env env,
                                   napi_value async_resource,
                                   napi_value async_resource_name,
                                   napi_async_execute_callback execute,
                                   napi_async_complete_callback complete,
                                   void* data,
                                   napi_async_work* result);

参数说明:

[in] env: 传入接口调用者的环境,包含js引擎等,由框架提供。

[in] async_resource: 可选项,关联async_hooks。

[in] async_resource_name: 异步资源标识符,主要用于async_hooks API暴露断言诊断信息。

[in] execute: 异步工作项的处理函数,当工作项被调度到后在worker线程中执行,用于处理业务逻辑,并得到结果。

[in] complete: execute参数指定的函数执行完成或取消后,触发执行该函数,将结果返回给JS。此函数在EventLoop中执行。

[in] data: 异步工作项上下文数据(Addon),用于在主线程接口实现方法、execute、complete之间传递数据。

[out] result: napi_async_work*指针,返回创建的异步工作项对象。

返回值:返回napi_ok表示转换成功,其他值失败

异步线程的处理由execute和complete两个自定义函数实现。data即是execute和complete两个自定义函数的主要传入参数。data的结构可以自定义结构。

// data类型定义
class NAsyncContext {
public:
    UniError err_;
    NVal res_;
    NContextCBExec cbExec_;
    NContextCBComplete cbComplete_;
    napi_async_work awork_;
    NRef thisPtr_;

    explicit NAsyncContext(NVal thisPtr) : err_(0), res_(NVal()), cbExec_(nullptr),
        cbComplete_(nullptr), awork_(nullptr), thisPtr_(thisPtr) {}
    virtual ~NAsyncContext() = default;
};

callback方式的处理方式

执行napi_create_async_work异步线程后,通过napi_queue_async_work将创建的async work添加到队列,由NAPI框架的底层去调度后执行。

// foundation/filemanagement/file_api/interfaces/kits/js/src/common/napi/n_async/n_async_work_callback.cpp
NVal NAsyncWorkCallback::Schedule(string procedureName, NContextCBExec cbExec, NContextCBComplete cbComplete)
{
    if (!ctx_->cb_ || !ctx_->cb_.Deref(env_).TypeIs(napi_function)) {
        UniError(EINVAL).ThrowErr(env_, "The callback shall be a funciton");
        return NVal();
    }
	// data内成员赋值
    ctx_->cbExec_ = move(cbExec);
    ctx_->cbComplete_ = move(cbComplete);

    napi_value resource = NVal::CreateUTF8String(env_, procedureName).val_;

    napi_status status =
        napi_create_async_work(env_, nullptr, resource, CallbackExecute, CallbackComplete, ctx_, &ctx_->awork_);
    if (status != napi_ok) {
        HILOGE("INNER BUG. Failed to create async work for %{public}d", status);
        return NVal();
    }
    status = napi_queue_async_work(env_, ctx_->awork_);// 创建的async work添加到队列
    if (status != napi_ok) {
        HILOGE("INNER BUG. Failed to queue async work for %{public}d", status);
        return NVal();
    }

    ctx_ = nullptr; // The ownership of ctx_ has been transfered
    return NVal::CreateUndefined(env_);
}

实现自定义的execute和complete,分别对应CallbackExecute, CallbackComplete。处理完成后将

// foundation/filemanagement/file_api/interfaces/kits/js/src/common/napi/n_async/n_async_work_callback.cpp
static void CallbackExecute(napi_env env, void *data)
{
...
}

static void CallbackComplete(napi_env env, napi_status status, void *data)
{
...
    //callback为js/ets传入的回调函数
    napi_value callback = ctx->cb_.Deref(env).val_;
...
    napi_status stat = napi_call_function(env, global, callback, argv.size(), argv.data(), &tmp);
    if (stat != napi_ok) {
        HILOGE("Failed to call function for %{public}d", stat);
    }
    napi_close_handle_scope(env, scope);
    napi_delete_async_work(env, ctx->awork_);
    delete ctx;
}

此处的ctx->cb_成员赋值,由NVal与NAsyncWorkCallback的两个构造函数完成

// foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
napi_value JsSendFile(napi_env env, napi_callback_info info)
{
...
    if (funcArg.GetArgc() == DFS_ARG_CNT::FOUR) {
        return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbComplete).val_;
    } else if (funcArg.GetArgc() == DFS_ARG_CNT::FIVE) {
        NVal cb(env, funcArg[DFS_ARG_POS::FIFTH]);
        return NAsyncWorkCallback(env, thisVar, cb).Schedule(procedureName, cbExec, cbComplete).val_;
    }
...
}

至此NAPI的异步回调流程完成。

后续更精彩

1.Openharmony应用NAPI详解--进阶篇2

标签:status,Openharmony,callback,--,work,进阶篇,env,async,napi
From: https://blog.51cto.com/u_15304012/6216905

相关文章

  • 如何做好今天的运维
    近年来很多声音讨论运维岗是不是会被替代的问题。但随着ChatGPT的出现和更多大模型的发布,似乎每个岗位都应该考虑下自己被替代的问题了。无论未来如何变化,现实是今天的我们仍然需要做好自己的工作,站好自己的岗。今天我将分享我所认为的优秀运维和架构师应该是怎样的。最后是关于运......
  • 删除Teamcenter bmide模板
    1.检验是否可以删除bmide_remove_template-u=infodba-p=infodba-g=dba-mode=dryrun-template=v7box BMIDE移除模板日期:周日4月23202308:37:46上午中国标准时间----------------------------------------------------------------------------验证输入参数...Fin......
  • Openharmony应用NAPI详解--进阶篇2
    NAPI面向C++的异步接口(promise)promise方式的处理方式承接上文,与callback方式不同的是,promise对象由C++侧创建以返回值的方式传递回js/ets侧,promise对象存储异步执行的结果。//foundation/filemanagement/file_api/interfaces/kits/js/src/common/napi/n_async/n_async_work_p......
  • sql注入getshell的几种方式
    文章目录一.intooutfile利用条件写入webshell二.--os-shell--sql-shell    介绍几种利用sql注入获取系统权限的方法,一是利用outfile函数,另外一种是利用--os-shell。一.intooutfile  利用条件:    1.此方法利用的先决条件web目录具有写权限,能够......
  • 如何在交互式环境中执行Python程序
    相信接触过Python的小伙伴们都知道运行Python脚本程序的方式有多种,目前主要的方式有:交互式环境运行、命令行窗口运行、开发工具上运行等,其中在不同的操作平台上还互不相同。今天,小编讲些Python基础的内容,以Windows下交互式环境为依托,演示Python程序的运行。一般来说,顺利安装Python......
  • uiautomator2+app ui自动化用例报错截图pytest_runtest_makereport
    pytest提供了pytest_runtest_makereport这个方法,可以捕获用例的执行情况。根据官方提供的示例,在conftest.py文件中添加如下代码就可以捕获每个用例的执行结果。那么pytest_runtest_makereport作用:对于给定的测试用例(item)和调用步骤(call),返回一个测试报告对象(_pytest.runne......
  • 为什么单片机编程放不下超过32万的整数?
    因为你的单片机可能是16位的,c语言16位编译器的int类型占2字节,也就是范围:-2^15~2^15-1 (-32768~32767)。32位的编译器int类型占4字节。这种情况下可以使用longint(16位编译器4字节),也可以使用循环处理整数。 ......
  • 手把手教你进行Scrapy中item类的实例化操作
     接下来我们将在爬虫主体文件中对Item的值进行填充。1、首先在爬虫主体文件中将Item模块导入进来,如下图所示。2、第一步的意思是说将items.py中的ArticleItem类导入到爬虫主体文件中去,将两个文件串联起来,其中items.py的部分内容如下图所示。3、将这个ArticleItem类导入之后,接下来......
  • 多通道振弦传感器无线采集仪 多类型数字传感器独立发送协议
    河北稳控科技多通道振弦传感器无线采集仪多类型数字传感器独立发送协议 独立发送传感器数据时,每个传感器是一个独立的数据包,发送至预设的TCP服务器。数据包字符串,结构说明如下:UDID>MDS+传感器类型码+第x个传感器>第x包/总x包>传感器数据,校验和字符,回车换行符例如:“......
  • 手把手教你使用Python网络爬虫获取菜谱信息
    今日鸡汤一腔热血勤珍重,洒去犹能化碧涛。/1前言/    在放假时,经常想尝试一下自己做饭,下厨房这个网址是个不错的选择。    下厨房是必选的网址之一,主要提供各种美食做法以及烹饪技巧。包含种类很多。    今天教大家去爬取下厨房的菜谱,保存在world文档,方便日后制作自......