当前位置:   article > 正文

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

napi使用

NAPI面向C++的异步接口

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

  • 同步返回结果给js/ets调用者

  • 另起线程完成异步操作

  • 通过回调(callback)或Promise将异步操作结果返回

4.异步接口

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

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

  • 函数声明

  1. // foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
  2. napi_value JsSendFile(napi_env env, napi_callback_info info)
  3. {
  4. ......
  5. }
  • 获取入参

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

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

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

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

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

  1. bool NFuncArg::InitArgs(std::function<bool()> argcChecker)
  2. {
  3. SetArgc(0);
  4. argv_.reset();
  5. size_t argc;
  6. napi_value thisVar;
  7. napi_status status = napi_get_cb_info(env_, info_, &argc, nullptr, &thisVar, nullptr);
  8. if (status != napi_ok) {
  9. HILOGE("Cannot get num of func args for %{public}d", status);
  10. return false;
  11. }
  12. if (argc) {
  13. argv_ = make_unique<napi_value[]>(argc);
  14. status = napi_get_cb_info(env_, info_, &argc, argv_.get(), &thisVar, nullptr);
  15. if (status != napi_ok) {
  16. HILOGE("Cannot get func args for %{public}d", status);
  17. return false;
  18. }
  19. }
  20. SetArgc(argc);
  21. SetThisVar(thisVar);
  22. return argcChecker();
  23. }

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

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

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

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

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

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

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

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

  1. // foundation/filemanagement/file_api/interfaces/kits/js/src/common/napi/n_async/n_async_work_callback.cpp
  2. NVal NAsyncWorkCallback::Schedule(string procedureName, NContextCBExec cbExec, NContextCBComplete cbComplete)
  3. {
  4. ...
  5. napi_status status =
  6. napi_create_async_work(env_, nullptr, resource, CallbackExecute, CallbackComplete, ctx_, &ctx_->awork_);
  7. if (status != napi_ok) {
  8. HILOGE("INNER BUG. Failed to create async work for %{public}d", status);
  9. return NVal();
  10. }
  11. ...
  12. }
  13. // foundation/filemanagement/file_api/interfaces/kits/js/src/common/napi/n_async/n_async_work_promise.cpp
  14. NVal NAsyncWorkPromise::Schedule(string procedureName, NContextCBExec cbExec, NContextCBComplete cbComplete)
  15. {
  16. ...
  17. napi_value resource = NVal::CreateUTF8String(env_, procedureName).val_;
  18. status = napi_create_async_work(env_, nullptr, resource, PromiseOnExec, PromiseOnComplete, ctx_, &ctx_->awork_);
  19. if (status != napi_ok) {
  20. HILOGE("INNER BUG. Failed to create async work for %{public}d", status);
  21. return NVal();
  22. }
  23. ...
  24. }

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

  1. napi_status napi_create_async_work(napi_env env,
  2. napi_value async_resource,
  3. napi_value async_resource_name,
  4. napi_async_execute_callback execute,
  5. napi_async_complete_callback complete,
  6. void* data,
  7. 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的结构可以自定义结构。

  1. // data类型定义
  2. class NAsyncContext {
  3. public:
  4. UniError err_;
  5. NVal res_;
  6. NContextCBExec cbExec_;
  7. NContextCBComplete cbComplete_;
  8. napi_async_work awork_;
  9. NRef thisPtr_;
  10. explicit NAsyncContext(NVal thisPtr) : err_(0), res_(NVal()), cbExec_(nullptr),
  11. cbComplete_(nullptr), awork_(nullptr), thisPtr_(thisPtr) {}
  12. virtual ~NAsyncContext() = default;
  13. };

callback方式的处理方式

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

  1. // foundation/filemanagement/file_api/interfaces/kits/js/src/common/napi/n_async/n_async_work_callback.cpp
  2. NVal NAsyncWorkCallback::Schedule(string procedureName, NContextCBExec cbExec, NContextCBComplete cbComplete)
  3. {
  4. if (!ctx_->cb_ || !ctx_->cb_.Deref(env_).TypeIs(napi_function)) {
  5. UniError(EINVAL).ThrowErr(env_, "The callback shall be a funciton");
  6. return NVal();
  7. }
  8. // data内成员赋值
  9. ctx_->cbExec_ = move(cbExec);
  10. ctx_->cbComplete_ = move(cbComplete);
  11. napi_value resource = NVal::CreateUTF8String(env_, procedureName).val_;
  12. napi_status status =
  13. napi_create_async_work(env_, nullptr, resource, CallbackExecute, CallbackComplete, ctx_, &ctx_->awork_);
  14. if (status != napi_ok) {
  15. HILOGE("INNER BUG. Failed to create async work for %{public}d", status);
  16. return NVal();
  17. }
  18. status = napi_queue_async_work(env_, ctx_->awork_);// 创建的async work添加到队列
  19. if (status != napi_ok) {
  20. HILOGE("INNER BUG. Failed to queue async work for %{public}d", status);
  21. return NVal();
  22. }
  23. ctx_ = nullptr; // The ownership of ctx_ has been transfered
  24. return NVal::CreateUndefined(env_);
  25. }

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

  1. // foundation/filemanagement/file_api/interfaces/kits/js/src/common/napi/n_async/n_async_work_callback.cpp
  2. static void CallbackExecute(napi_env env, void *data)
  3. {
  4. ...
  5. }
  6. static void CallbackComplete(napi_env env, napi_status status, void *data)
  7. {
  8. ...
  9. //callback为js/ets传入的回调函数
  10. napi_value callback = ctx->cb_.Deref(env).val_;
  11. ...
  12. napi_status stat = napi_call_function(env, global, callback, argv.size(), argv.data(), &tmp);
  13. if (stat != napi_ok) {
  14. HILOGE("Failed to call function for %{public}d", stat);
  15. }
  16. napi_close_handle_scope(env, scope);
  17. napi_delete_async_work(env, ctx->awork_);
  18. delete ctx;
  19. }

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

  1. // foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
  2. napi_value JsSendFile(napi_env env, napi_callback_info info)
  3. {
  4. ...
  5. if (funcArg.GetArgc() == DFS_ARG_CNT::FOUR) {
  6. return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbComplete).val_;
  7. } else if (funcArg.GetArgc() == DFS_ARG_CNT::FIVE) {
  8. NVal cb(env, funcArg[DFS_ARG_POS::FIFTH]);
  9. return NAsyncWorkCallback(env, thisVar, cb).Schedule(procedureName, cbExec, cbComplete).val_;
  10. }
  11. ...
  12. }

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

后续更精彩

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/92046
推荐阅读
相关标签
  

闽ICP备14008679号