napi 基本使用场景示例

napi 基本使用场景示例

HarmonyOS
2024-05-23 21:33:18
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
牙膏只用咸的

一. native 与 ts 异步调用 (async_library)

1.1 通过libuv接口

通过libuv 的方式将JS任务抛到loop中执行,即回到主线程执行

(1)定义变量保存env

struct CallbackContext { 
   napi_env env = nullptr; 
   napi_ref callbackRef = nullptr; 
   int retData = 0; 
};

(2)js线程保存env

napi_status napi_get_cb_info(napi_env env, 
                            napi_callback_info cbinfo, 
                            size_t* argc, 
                            napi_value* argv, 
                            napi_value* thisArg, 
                            void** data) 

[in] env:调用 API 的环境。

[in] cbinfo:传递给回调函数的回调信息。

[in-out] argc:指定所提供的 argv 数组的长度并接收参数的实际计数。argc 可以选择性地通过传递 NULL 来忽略。

[out] argv:参数将被复制到的 napi_value 的 C 数组。如果参数数量多于提供的数量,则只复制请求数量的参数。如果提供的参数比声明的少,则 argv 的其余部分将填充代表 undefined 的 napi_value 值。argv 可以选择性地通过传递 NULL 来忽略。

[out] thisArg:接收调用的 JavaScript this 参数。thisArg 可以选择性地通过传递 NULL 来忽略。

[out] data:接收回调的数据指针。data 可以选择性地通过传递 NULL 来忽略。

napi_status napi_create_reference(napi_env env, 
                                             napi_value value, 
                                             uint32_t initial_refcount, 
                                             napi_ref* result); 

[in] env:调用 API 的环境。

[in] value:正在为其创建引用的 napi_value。

[in] initial_refcount:新引用的初始引用计数。

[out] result:napi_ref 指向新的引用。

napi_value UVCallbackWorker::JSTest(napi_env env, napi_callback_info info)  
{ 
    std::thread::id this_id = std::this_thread::get_id(); 
    OH_LOG_INFO(LOG_APP, "thread id0  %{public}d.\n", this_id); 
 
    size_t argc = 1; 
    napi_value argv[1] = {0}; 
    napi_value thisVar = nullptr; 
    void *data = nullptr; 
    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); 
 
    napi_valuetype valueType = napi_undefined; 
    napi_typeof(env, argv[0], &valueType); 
    if (valueType != napi_function) { 
        return nullptr; 
    } 
    // 这里缓存了env 
    auto asyncContext = new CallbackContext(); 
    asyncContext->env = env; 
    napi_create_reference(env, argv[0], 1, &asyncContext->callbackRef); 
 
    std::thread testThread(callbackTest, asyncContext); 
    testThread.detach(); 
 
    std::thread::id this_id2 = std::this_thread::get_id(); 
    OH_LOG_INFO(LOG_APP, "thread id3  %{public}d.\n", this_id2); 
 
    return nullptr; 
}

(3) 调用libuv接口抛JS任务到loop中执行。

在JavaScript中,通常使用事件循环(event loop)来处理异步任务。事件循环是一种机制,它允许JavaScript引擎处理多个任务并按照它们的优先级执行。事件循环主要由一个无限循环组成,该循环会不断地从任务队列中取出任务并执行。 当一个异步任务被触发时,它会被添加到任务队列中。任务队列是一个先进先出的队列,即先添加到队列中的任务先被执行。在JavaScript中,有多种类型的任务队列,例如宏任务队列和微任务队列。 loop指的是事件循环的这个无限循环,它不断地从任务队列中取出任务并执行,直到任务队列为空。当所有任务都被执行完毕后,事件循环会暂停,等待新的任务被添加到任务队列中。

UV_EXTERN int uv_queue_work(uv_loop_t* loop, 
                           uv_work_t* req, 
                           uv_work_cb work_cb, 
                           uv_after_work_cb after_work_cb);
void UVCallbackWorker::callbackTest(CallbackContext *context)  
{ 
    std::thread::id this_id = std::this_thread::get_id(); 
    OH_LOG_INFO(LOG_APP, "thread id1  %{public}d.\n", this_id); 
    uv_loop_s *loop = nullptr; 
    // 此处的env需要在注册JS回调时保存下来。从env中获取对应的JS线程的loop。 
    napi_get_uv_event_loop(context->env, &loop); 
 
    // 创建uv_work_t用于传递私有数据,注意回调完成后需要释放内存,此处省略生成回传数据的逻辑,传回int类型1。 
    uv_work_t *work = new uv_work_t; 
    context->retData = 1; 
    work->data = (void *)context; 
 
 
    // 调用libuv接口抛JS任务到loop中执行。 
    uv_queue_work( 
        loop, work, 
        // 此回调在另一个普通线程中执行,用于处理异步任务,回调执行完后执行下面的回调。本场景下该回调不需要执行任务。 
        [](uv_work_t *work) {}, 
        // 此回调会在env对应的JS线程中执行。 
        [](uv_work_t *work, int status) { 
            CallbackContext *context = (CallbackContext *)work->data; 
            napi_handle_scope scope = nullptr; 
            // 打开handle scope用于管理napi_value的生命周期,否则会内存泄露。 
            napi_open_handle_scope(context->env, &scope); 
            if (scope == nullptr) { 
                return; 
            } 
            napi_value callback = nullptr; 
            napi_get_reference_value(context->env, context->callbackRef, &callback); 
            napi_value retArg; 
            napi_create_int32(context->env, context->retData, &retArg); 
            napi_value ret; 
            napi_call_function(context->env, nullptr, callback, 1, &retArg, &ret); 
            napi_delete_reference(context->env, context->callbackRef); 
 
            // 关闭handle scope释放napi_value。 
            napi_close_handle_scope(context->env, scope); 
 
            std::thread::id this_id = std::this_thread::get_id(); 
            OH_LOG_INFO(LOG_APP, "thread id2  %{public}d.\n", this_id); 
 
            // 释放work指针。 
            if (work != nullptr) { 
                delete work; 
            } 
            delete context; 
        }); 
}

1.2通过napi_create_async_work方式

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:调用 API 的环境。

[in] async_resource:与将传递给可能的 async_hooks init 钩子的异步工作关联的可选对象。

[in] async_resource_name:为 async_hooks API 公开的诊断信息提供的资源类型的标识符。

[in] execute:应调用以异步执行逻辑的原生函数。给定的函数从工作池线程调用,可以与主事件循环线程并行执行。

[in] complete:异步逻辑完成或取消时将调用的原生函数。从主事件循环线程调用给定的函数。napi_async_complete_callback提供了更多详细信息。

[in] data:用户提供的数据上下文。这将被传递回执行和完成功能。

[out] result:napi_async_work* 是新创建的异步工作的句柄。

使用callback回调

定义一个变量用于数据的传递

struct AsyncCallbackData { 
  napi_async_work asyncWork = nullptr; 
  napi_ref callbackRef = nullptr; 
  double args[2] = {0}; 
  double result = 0; 
};

(1)执行异步任务回调:

void AsyncCallbackWorker::ExecuteCBWithCallback(napi_env env, void *data)  
{ 
  AsyncCallbackData *callbackData = reinterpret_cast<AsyncCallbackData *>(data); 
  callbackData->result = callbackData->args[0] + callbackData->args[1]; 

(2)异步任务完成回调:

void AsyncCallbackWorker::CompleteCBWithCallback(napi_env env, napi_status status, void *data)  
{ 
  AsyncCallbackData *callbackData = reinterpret_cast<AsyncCallbackData *>(data); 
  napi_value callbackArg[1] = {nullptr}; 
  napi_create_double(env, callbackData->result, &callbackArg[0]); 
  napi_value callback = nullptr; 
  napi_get_reference_value(env, callbackData->callbackRef, &callback); 
  // 执行回调函数 
  napi_value result; 
  napi_value undefined; 
  napi_get_undefined(env, &undefined); 
  napi_call_function(env, undefined, callback, 1, callbackArg, &result); 
  // 删除napi_ref对象以及异步任务 
  napi_delete_reference(env, callbackData->callbackRef); 
  napi_delete_async_work(env, callbackData->asyncWork); 
  delete callbackData; 
}

(3)napi_create_async_work 创建异步任务

napi_value AsyncCallbackWorker::AsyncWorkWithCallback(napi_env env, napi_callback_info info)  
{ 
  size_t argc = 3; 
  napi_value args[3]; 
  napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
  auto asyncContext = new AsyncCallbackData(); 
  // 将接收到的参数保存到callbackData 
  napi_get_value_double(env, args[0], &asyncContext->args[0]); 
  napi_get_value_double(env, args[1], &asyncContext->args[1]); 
  napi_create_reference(env, args[2], 1, &asyncContext->callbackRef); 
  napi_value resourceName = nullptr; 
  napi_create_string_utf8(env, "asyncWorkCallback", NAPI_AUTO_LENGTH, &resourceName); 
  // 创建异步任务 
  napi_create_async_work(env, nullptr, resourceName, ExecuteCBWithCallback, CompleteCBWithCallback, asyncContext, 
                          &asyncContext->asyncWork); 
  // 将异步任务加入队列 
  napi_queue_async_work(env, asyncContext->asyncWork); 
  return nullptr; 
}

使用 promise 回调

全局的变量

struct CallbackData { 
  napi_async_work asyncWork = nullptr; 
  napi_deferred deferred = nullptr; 
  napi_ref callback = nullptr; 
  double args = 0; 
  double result = 0; 
};

()执行异步任务回调:

void AsyncPromiseWorker::ExecuteCBWithPromise(napi_env env, void *data)  
{ 
  CallbackData *callbackData = reinterpret_cast<CallbackData *>(data); 
  callbackData->result = callbackData->args; 
}

()异步任务完成的回调:

void AsyncPromiseWorker::CompleteCBWithPromise(napi_env env, napi_status status, void *data)  
{ 
  CallbackData *callbackData = reinterpret_cast<CallbackData *>(data); 
  napi_value result = nullptr; 
  napi_create_double(env, callbackData->result, &result); 
  if (callbackData->result > 0) { 
      napi_resolve_deferred(env, callbackData->deferred, result); 
  } else { 
      napi_reject_deferred(env, callbackData->deferred, result); 
  } 
​ 
  napi_delete_async_work(env, callbackData->asyncWork); 
  delete callbackData; 
}

1. 创建异步任务

napi_value AsyncPromiseWorker::AsyncWorkWithPromise(napi_env env, napi_callback_info info)  
{ 
  size_t argc = 1; 
  napi_value args[1]; 
  napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
    
​ 
  // 一个新创建的延迟对象,稍后可以传递给 napi_resolve_deferred() 或 napi_reject_deferred() 以解析 resp。拒绝相关的 promise。 
  napi_deferred deferred = nullptr; 
  // 与延迟对象关联的 JavaScript promise。 
  napi_value promise = nullptr; 
  napi_create_promise(env, &deferred, &promise); 
​ 
// 用于保存promise 的变量 
  auto callbackData = new CallbackData(); 
  callbackData->deferred = deferred; 
  napi_get_value_double(env, args[0], &callbackData->args); 
​ 
  napi_value resourceName = nullptr; 
  napi_create_string_utf8(env, "AsyncCallback", NAPI_AUTO_LENGTH, &resourceName); 
  // 创建异步任务 
  napi_create_async_work(env, nullptr, resourceName, ExecuteCBWithPromise, CompleteCBWithPromise, callbackData, 
                          &callbackData->asyncWork); 
  // 将异步任务加入队列 
  napi_queue_async_work(env, callbackData->asyncWork); 
​ 
  return promise; 
}

二、native 与ts 同步调用(sync_library)

2.1 JS 和 C++ 相互调用ArrayBuffer 的方式

napi_status napi_create_arraybuffer(napi_env env, 
                                   size_t byte_length, 
                                   void** data, 
                                   napi_value* result) 

[in] env:调用 API 的环境。

[in] length:要创建的数组缓冲区的字节长度。

[out] data:指向 ArrayBuffer 的底层字节缓冲区的指针。data 可以选择性地通过传递 NULL 来忽略。

[out] result:代表 JavaScript ArrayBuffer 的 napi_value。

napi_status napi_create_typedarray(napi_env env, 
                                  napi_typedarray_type type, 
                                  size_t length, 
                                  napi_value arraybuffer, 
                                  size_t byte_offset, 
                                  napi_value* result) 

[in] env:调用 API 的环境。

[in] type:TypedArray 中元素的标量数据类型。

[in] length:TypedArray 中的元素数。

[in] arraybuffer:ArrayBuffer 是类型化数组的基础。

[in] byte_offset:ArrayBuffer 中开始投影 TypedArray 的字节偏移量。

[out] result:代表 JavaScript TypedArray 的 napi_value。

传到js 层

// NAPI层 array 传到js层 
static napi_value NAPI2TS(napi_env env, napi_callback_info info) { 
  // 数据个数 
  int num = 10; 
​ 
  // 创建output_buffer 
  napi_value output_buffer; 
  void *output_ptr = NULL; 
  napi_create_arraybuffer(env, num * sizeof(int32_t), &output_ptr, &output_buffer); 
​ 
  // output_array 
  napi_value output_array; 
  napi_create_typedarray(env, napi_int32_array, num, output_buffer, 0, &output_array); 
​ 
  // 给output_ptr、output_buffer赋值 
  int32_t *output_bytes = (int32_t *)output_ptr; 
  for (int32_t i = 0; i < num; i++) { 
      output_bytes[i] = i; 
  } 
​ 
  for (int32_t i = 0; i < num; i++) { 
      OH_LOG_INFO(LOG_APP, "NAPI2TS: C++ %{public}d", *((int32_t *)(output_ptr) + i)); 
  } 
​ 
  return output_array; 
}

2.1.2 ts 数组传到napi 层

static napi_value TS2NAPI(napi_env env, napi_callback_info info) { 
  // 获取TS层传来的参数 
  size_t argc = 1; 
  napi_value args; 
  napi_get_cb_info(env, info, &argc, &args, NULL, NULL); 
  napi_value input_array = args; 
​ 
  // 获取传入数组typedarray生成input_buffer 
  napi_typedarray_type type; // 数据类型 
  napi_value input_buffer; 
  size_t byte_offset; // 数据偏移 
  size_t i, length;   // 数据字节大小 
  napi_get_typedarray_info(env, input_array, &type, &length, NULL, &input_buffer, &byte_offset); 
​ 
  // 获取数组数据z 
  void *data; 
  size_t byte_length; 
  napi_get_arraybuffer_info(env, input_buffer, &data, &byte_length); 
​ 
  if (type == napi_int32_array) { 
      int32_t *data_bytes = (int32_t *)(data); 
      for (i = 0; i < length / sizeof(int32_t); i++) { 
          OH_LOG_INFO(LOG_APP, "TS2NAPI: C++ %{public}d", *((int32_t *)(data_bytes) + i)); 
      } 
  } 
​ 
  return NULL; 
}

2.2.2 native 与ts 相互调用

TS 层对象

export class testCb{ 
 testNum:number = 0; // 源码中未初始化 
 testString:string = 'hello world'; // 源码中未初始化 
}; 
​ 
export interface  cb{ 
 // 回调一个string回TS 
 onCallBack1(id: string):void; 
 // 回调一个number回TS 
 onCallBack2(cnt:number):void; 
 // 回调一个类或者结构体回TS 
 onCallBack3(cbClass:testCb):void; 
} 
​ 
class tsClass implements  cb { 
 // private static instance: tsClass = new tsClass(); 
​ 
 public onCallBack1(id: string){ 
   //操作id 
   console.log("testcallback1" + id) 
} 
 public onCallBack2(cnt: number){ 
   // 操作number 
   console.log("testcallback2" + cnt) 
} 
 public onCallBack3(cbClass: testCb){ 
   // 操作cbClass 
   console.log("testcallback3" + cbClass.testNum) 
} 
 // public static getInstance() { 
 //   return this.instance; 
 // } 
}

C++ 侧接受传入的JTS对象,并在C++ 层创建对象作为JS对象的函数参数传给JS层

static napi_value TransObect(napi_env env, napi_callback_info info) { 
   napi_status status; 
​ 
   size_t argc = 1; 
   napi_value js_cb; 
   // 获取TS侧传入的参数 
   status = napi_get_cb_info(env, info, &argc, &js_cb, nullptr, nullptr); 
   assert(status == napi_ok); 
​ 
   // 获取TS 对象的方法 onCallBack1 
   napi_value onCallBack1, str_arg; 
   status = napi_get_named_property(env, js_cb, "onCallBack1", &onCallBack1); 
   assert(status == napi_ok); 
​ 
   status = napi_create_string_utf8(env, "mangguo", strlen("mangguo"), &str_arg); 
   assert(status == napi_ok); 
​ 
   // 调用TS 对象的方法 onCallBack1 
   size_t cb1argc = 1; 
   status = napi_call_function(env, js_cb, onCallBack1, cb1argc, &str_arg, nullptr); 
​ 
   // 获取TS 对象的方法 onCallBack2 
   size_t cb2argc = 1; 
   napi_value onCallBack2, int_arg; 
   status = napi_get_named_property(env, js_cb, "onCallBack2", &onCallBack2); 
​ 
   status = napi_create_int32(env, 2, &int_arg); 
   assert(status == napi_ok); 
​ 
   // 调用TS 对象的方法 onCallBack2 
   status = napi_call_function(env, js_cb, onCallBack2, cb2argc, &int_arg, nullptr); 
   assert(status == napi_ok); 
​ 
   // 获取TS 对象的方法 onCallBack2 
   napi_value oncallback3, arg_object; 
   status = napi_get_named_property(env, js_cb, "onCallBack3", &oncallback3); 
   assert(status == napi_ok); 
​ 
   // native 层创建对象arg_object 
   status = napi_create_object(env, &arg_object); 
   assert(status == napi_ok); 
​ 
   napi_value testNum,testString,cb3argc; 
   status = napi_create_int32(env, 123, &testNum); 
   assert(status == napi_ok); 
   // 给上面创建的arg_object对象属性testNum赋值123 
   status = napi_set_named_property(env, arg_object, "testNum", testNum); 
   assert(status == napi_ok); 
​ 
   status = napi_create_string_utf8(env, "mangguo", strlen("mangguo"), &testString); 
   assert(status == napi_ok); 
   // 给上面创建的arg_object对象属性testString赋值mangguo 
   status = napi_set_named_property(env, arg_object, "testString", testString); 
   assert(status == napi_ok); 
   // 调用TS 对象的方法 onCallBack3,并将上面创建的对象arg_object,作为方法参数传递 
   status = napi_call_function(env, js_cb, oncallback3, cb2argc, &arg_object, nullptr); 
   assert(status == napi_ok); 
​ 
   return nullptr; 

TS 侧调用如下:

let tscb: tsClass = new tsClass(); 
testNapi.transobject(tscb);
分享
微博
QQ
微信
回复
2024-05-24 16:25:54
相关问题
HarmonyOS Navigator的使用场景
73浏览 • 1回复 待解决
关于emitter、eventHub的使用场景
2659浏览 • 1回复 待解决
请问ArkTS中this使用场景是什么?
2012浏览 • 1回复 待解决
HarmonyOS Navigation和router的使用场景
429浏览 • 2回复 待解决
ArkGraphics 2D都有哪些使用场景
651浏览 • 1回复 待解决
HAR和HSP的使用场景区分
432浏览 • 1回复 待解决
应用启动框架AppStartup应用场景分析
554浏览 • 1回复 待解决
有哪些应用场景
3732浏览 • 1回复 待解决
HarmonyOS onDidBuild的作用和应用场景
99浏览 • 1回复 待解决
基于mysql的悲观锁的运用场景
2638浏览 • 1回复 待解决
JSVM-API的应用场景有哪些?
426浏览 • 1回复 待解决