Module Registration
First, we need to register the native module. This is done in the queue_work.cpp file:
// queue_work.cpp
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{ "queueWork", nullptr, QueueWork, nullptr, nullptr, nullptr, napi_default, nullptr }
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_module nativeModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "queue_work",
.nm_priv = nullptr,
.reserved = { 0 },
};
extern "C" __attribute__((constructor)) void RegisterQueueWorkModule() {
napi_module_register(&nativeModule);
}
Queuing Work to the ArkTS Thread
Next, we need to get the event loop associated with the NAPI environment and queue a task to be executed on the ArkTS thread. This is also done in the queue_work.cpp file:
// queue_work.cpp
#include <thread>
#include <napi/native_api.h>
#include <uv.h>
struct CallbackContext {
napi_env env = nullptr;
napi_ref callbackRef = nullptr;
int32_t retData = 0;
};
void StartThread(CallbackContext* context) {
uv_loop_s* loop = nullptr;
napi_get_uv_event_loop(context->env, &loop);
// Create a uv_work_t to pass private data
uv_work_t* work = new uv_work_t;
context->retData = 1;
work->data = context;
// Queue the work to the ArkTS thread
uv_queue_work(
loop,
work,
[](uv_work_t* work) {},
[](uv_work_t* work, int status) {
CallbackContext* context = reinterpret_cast<callbackcontext>(work->data);
napi_handle_scope scope = nullptr;
napi_open_handle_scope(context->env, &scope);
if (scope == nullptr) {
return;
}
// Call the ArkTS function
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);
// Close the handle scope
napi_close_handle_scope(context->env, scope);
if (work != nullptr) {
delete work;
}
delete context;
}
);
}
static napi_value QueueWork(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value argv[1] = {nullptr};
napi_value thisVar = nullptr;
void* data = nullptr;
napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
// Parameter check
if (argc < 1) {
napi_throw_error(env, "ParameterError", "one param expected");
return nullptr;
}
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[0], &valueType);
if (valueType != napi_function) {
napi_throw_error(env, "ParameterError", "function expected");
return nullptr;
}
// Save the env and callback for later use
auto asyncContext = new CallbackContext();
asyncContext->env = env;
napi_create_reference(env, argv[0], 1, &asyncContext->callbackRef);
std::thread testThread(StartThread, asyncContext);
testThread.detach();
return nullptr;
}
</callbackcontext>
ArkTS Side Example Code
On the ArkTS side, you can call the queueWork function as follows:
import { queueWork } from 'libqueue_work.so';
queueWork((result: number) => {
console.log("result = " + result);
});