Node js 原生模块,从 objective-c 块事件侦听器触发回调不起作用
Node js native module, triggering callback from objective-c block event listener doesn't work
我需要创建一个本机节点模块来监听 objective-c OSX 事件,并在每次发生时触发对 javascript 的回调:
nativeAddon.listen(() => {
console.log('It works!')
})
回调在 setUpCallback
函数中立即调用时有效,但它不会从 objective-c 观察者块中触发。
这是我的 main.mm 文件的样子
using namespace v8;
Local<Function> event_callback;
void setUpCallback(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
// Store the callback to be used in the runCallback function
Local<Function> cb = Local<Function>::Cast(args[0]);
event_callback = cb;
// THIS WORKS
runCallback();
// Listen to a mac event and trigger the callback when it happens
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserverForName:NSWorkspaceActiveSpaceDidChangeNotification object:NULL queue:NULL usingBlock:^(NSNotification *note) {
// THIS DOESN'T WORK
runCallback();
}];
}
void runCallback() {
auto isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
Local<Context> context = isolate->GetCurrentContext();
Local<Value> argv[1] = { String::NewFromUtf8(isolate, "hello world", NewStringType::kNormal).ToLocalChecked() };
auto fn = Local<Function>::New(isolate, event_callback);
fn->Call(context, Null(isolate), 1, argv).ToLocalChecked();
}
void Initialize(Local<Object> exports) {
NODE_SET_METHOD(exports, "listen", setUpCallback);
}
NODE_MODULE(addon, Initialize)
如有任何帮助,我们将不胜感激!
尝试将 event_callback
的类型从 Local<Function>
更改为 Persistent<Function>
。
Local
的生命周期与创建它的 HandleScope
的生命周期相关。一旦 HandleScope
超出范围(在您的情况下,在function setUpCallback
),所有在它处于活动状态时创建的 Locals 都将失效。如果你需要一个在 HandleScope
消失后 仍然存在 的句柄,你需要使用 Persistent
。此处有更多详细信息:https://v8.dev/docs/embed#advanced-guide.
我正在使用 node-addon-api
并尝试了 @jmrk
的解决方案,但它对我不起作用。将类型更改为“持久”是不够的。
所以我在 GitHub here 上问了这个问题。
因为Mac事件是在单独的线程中触发的,所以需要使用ThreadSafeFunction
.
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#include <napi.h>
Napi::ThreadSafeFunction tsfn;
// Trigger the JS callback when active space changes
void listenForActiveSpaceChange(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
// Create a ThreadSafeFunction
tsfn = Napi::ThreadSafeFunction::New(
env,
info[0].As<Napi::Function>(), // JavaScript function called asynchronously
"Active Space", // Name
0, // Unlimited queue
1 // Only one thread will use this initially
);
// Create a native callback function to be invoked by the TSFN
auto callback = [](Napi::Env env, Napi::Function jsCallback) {
// Call the JS callback
jsCallback.Call({});
};
// Subscribe to macOS spaces change event
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserverForName:NSWorkspaceActiveSpaceDidChangeNotification
object:NULL
queue:NULL
usingBlock:^(NSNotification *note) {
// Perform a blocking call
napi_status status =
tsfn.BlockingCall(callback);
if (status != napi_ok) {
NSLog(@"Something went wrong, BlockingCall failed");
}
}];
}
Napi::Object init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "listenForActiveSpaceChange"),
Napi::Function::New(env, listenForActiveSpaceChange));
return exports;
};
NODE_API_MODULE(mac_helper, init);
我需要创建一个本机节点模块来监听 objective-c OSX 事件,并在每次发生时触发对 javascript 的回调:
nativeAddon.listen(() => {
console.log('It works!')
})
回调在 setUpCallback
函数中立即调用时有效,但它不会从 objective-c 观察者块中触发。
这是我的 main.mm 文件的样子
using namespace v8;
Local<Function> event_callback;
void setUpCallback(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
// Store the callback to be used in the runCallback function
Local<Function> cb = Local<Function>::Cast(args[0]);
event_callback = cb;
// THIS WORKS
runCallback();
// Listen to a mac event and trigger the callback when it happens
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserverForName:NSWorkspaceActiveSpaceDidChangeNotification object:NULL queue:NULL usingBlock:^(NSNotification *note) {
// THIS DOESN'T WORK
runCallback();
}];
}
void runCallback() {
auto isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
Local<Context> context = isolate->GetCurrentContext();
Local<Value> argv[1] = { String::NewFromUtf8(isolate, "hello world", NewStringType::kNormal).ToLocalChecked() };
auto fn = Local<Function>::New(isolate, event_callback);
fn->Call(context, Null(isolate), 1, argv).ToLocalChecked();
}
void Initialize(Local<Object> exports) {
NODE_SET_METHOD(exports, "listen", setUpCallback);
}
NODE_MODULE(addon, Initialize)
如有任何帮助,我们将不胜感激!
尝试将 event_callback
的类型从 Local<Function>
更改为 Persistent<Function>
。
Local
的生命周期与创建它的 HandleScope
的生命周期相关。一旦 HandleScope
超出范围(在您的情况下,在function setUpCallback
),所有在它处于活动状态时创建的 Locals 都将失效。如果你需要一个在 HandleScope
消失后 仍然存在 的句柄,你需要使用 Persistent
。此处有更多详细信息:https://v8.dev/docs/embed#advanced-guide.
我正在使用 node-addon-api
并尝试了 @jmrk
的解决方案,但它对我不起作用。将类型更改为“持久”是不够的。
所以我在 GitHub here 上问了这个问题。
因为Mac事件是在单独的线程中触发的,所以需要使用ThreadSafeFunction
.
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#include <napi.h>
Napi::ThreadSafeFunction tsfn;
// Trigger the JS callback when active space changes
void listenForActiveSpaceChange(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
// Create a ThreadSafeFunction
tsfn = Napi::ThreadSafeFunction::New(
env,
info[0].As<Napi::Function>(), // JavaScript function called asynchronously
"Active Space", // Name
0, // Unlimited queue
1 // Only one thread will use this initially
);
// Create a native callback function to be invoked by the TSFN
auto callback = [](Napi::Env env, Napi::Function jsCallback) {
// Call the JS callback
jsCallback.Call({});
};
// Subscribe to macOS spaces change event
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserverForName:NSWorkspaceActiveSpaceDidChangeNotification
object:NULL
queue:NULL
usingBlock:^(NSNotification *note) {
// Perform a blocking call
napi_status status =
tsfn.BlockingCall(callback);
if (status != napi_ok) {
NSLog(@"Something went wrong, BlockingCall failed");
}
}];
}
Napi::Object init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "listenForActiveSpaceChange"),
Napi::Function::New(env, listenForActiveSpaceChange));
return exports;
};
NODE_API_MODULE(mac_helper, init);