1.ts接口申明
index.d.ts
export const startDownload: (cbFn: (progress: number) => void) => void;
2. C/C++ 代码
typedef struct CallbackContext {
napi_env env = nullptr;
napi_ref callbackRef = nullptr;
int progress = 0;
} CallbackContext;
// Call back the ts side function to notify the progress information to the ts side.
static void callTS(napi_env env, napi_value jsCb, void *context, void *data) {
CallbackContext *arg = (CallbackContext *)data;
napi_value progress;
//使用CallbackContext中的数据,构造progress
napi_create_int32(arg->env, arg->progress, &progress);
//调用jsCb,参数为progress,1位参数个数
napi_call_function(arg->env, nullptr, jsCb, 1, &progress, nullptr);
}
// Simulate a download task. Because the ts side function is called here, a thread-safe function must be used.
void downloadTask(CallbackContext *context) {
if (context) {
napi_value workName;
napi_create_string_utf8(context->env, "download", NAPI_AUTO_LENGTH, &workName);
napi_value jsCb;
//获取回调对象,保存在jsCb中
napi_get_reference_value(context->env, context->callbackRef, &jsCb);
napi_threadsafe_function tsFn;
//创建tsFn,看起来jsCb无法直接使用。注意callTS有一个函数指针
napi_create_threadsafe_function(context->env, jsCb, nullptr, workName, 0, 1, nullptr, nullptr, nullptr, callTS, &tsFn);
int progressLength = 100;
while (context->progress < progressLength) {
context->progress += 1;
//napi调用tsFn函数,转到callTS函数
napi_acquire_threadsafe_function(tsFn);
napi_call_threadsafe_function(tsFn, (void *)context, napi_tsfn_blocking);
std::this_thread::sleep_for(std::chrono::milliseconds(progressLength));
}
} else {
return;
}
napi_delete_reference(context->env, context->callbackRef);
delete context;
};
static napi_value startDownload(napi_env env, napi_callback_info info) {
//step 1: ts到达c/C++代码逻辑框架
size_t argc = 1;
napi_value args[1] = {nullptr};
//info由ts模块经napi框架传递到这里,里面包含了回调函数的信息
//只有一个回调函数,因此argc为1,args在这里被info中的值初始化
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
//构造一个CallbackContext,并与当前env绑定
auto asyncContext = new CallbackContext();
asyncContext->env = env;
//用args的值构造asyncContext的callbackRef,看起来c/C++通过callbackRef来完成后面的回调
napi_create_reference(env, args[0], 1, &asyncContext->callbackRef);
/*
* Start the download thread, perform the download task in the child thread and notify the progress to the arkTs
* thread in real time.
*/
//启动线程,在线程中使用回调接口
std::thread downloadThread(downloadTask, asyncContext);
downloadThread.detach();
//ts的接口调用,在这里完成调用。
return nullptr;
}
//startDownload回调函数组册
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{"startDownload", nullptr, startDownload, 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 callbackModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = ((void *)0),
.reserved = {0},
};
//模块构造
extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&callbackModule); }
desc[]数组定义了回调函数数组,这里只定义了一个回调函数;可以看到在在C/C++中实现了回调函数并不是以func定义,是定义为properties。napi_property_descriptor一个proterty的结构体,暂时还没有深究该结构体的内容;这里主要关注第一个字符串参数,需要与ts中申明的接口字符串一致。第三个函数指针参数指向一个C++的函数startDownload,倒数第二个参数定义了这个属性在napi框架中napi_property_attributes,可以参数该结构体的信息如下:
typedef enum {
napi_default = 0,
napi_writable = 1 << 0,
napi_enumerable = 1 << 1,
napi_configurable = 1 << 2,
// Used with napi_define_class to distinguish static properties
// from instance properties. Ignored by napi_define_properties.
napi_static = 1 << 10,
#if NAPI_VERSION >= 8
// Default for class methods.
napi_default_method = napi_writable | napi_configurable,
// Default for object properties, like in JS obj[prop].
napi_default_jsproperty = napi_writable |
napi_enumerable |
napi_configurable,
#endif // NAPI_VERSION >= 8
} napi_property_attributes;
通过以上代码,实现ts注册C/C++回调,并在c/C++中使用回调关键点包含以下信息:
1. napi_define_properties接口,注册napi_property_descriptor数组,napi_property_descriptor包含proterty,里面包含提供给ts的回调函数注册接口
- C/C++ 模块拿到ts模块传过来的回调接口,需要将接口转换为C/C++可以使用的回调主要步骤包含如下
-
napi_callback_info为ts传递过来的结构信息,需要使用napi_get_cb_info获取内部的回调接口,保存为napi_value类型,
- 由于回调不是立刻使用需要在合适的时候使用,这里将napi_value类型的回调使用napi_create_reference函数保存为一个napi_ref,在需要使用时,使用napi_get_reference_value函数获取napi_value;
- 因为回调可能是异步的,为了确保线程安全性,使用了特别的napi_create_threadsafe_function,创建了一个线程安全的环境,在专门的线程中处理回调。配合的线程安全的函数有napi_acquire_threadsafe_function,napi_call_threadsafe_function。
- 最后使用napi_call_function(arg->env, nullptr, jsCb, 1, &progress, nullptr);完成回调,jsCb为napi_value类型,progress为napi_value