React Native 0.66中如何获取JSCallInvoker?(MMKV二次开发)
问题:React Native 0.66(启用Hermes)中无法从RCTBridge获取JSCallInvoker
我在基于React Native 0.66(启用Hermes)二次开发react-native-mmkv库时,尝试访问React Native桥接中指向JS线程的JSCallInvoker方法,但在Xcode中找不到该方法的引用。当前通过[RCTBridge currentBridge]获取的bridge对象无法访问JSCallInvoker,想咨询是否需要添加额外头文件,或其他获取JSCallInvoker的方法。
相关代码如下:
#import "MmkvModule.h" #import "JSIUtils.h" #import <React/RCTBridge+Private.h> #import <ReactCommon/CallInvoker.h> #import <React/RCTUtils.h> #import <jsi/jsi.h> #import <MMKV/MMKV.h> #import "MmkvHostObject.h" using namespace facebook; @implementation MmkvModule RCT_EXPORT_MODULE(MMKV) + (NSString*)getPropertyAsStringOrNilFromObject:(jsi::Object&)object propertyName:(std::string)propertyName runtime:(jsi::Runtime&)runtime { jsi::Value value = object.getProperty(runtime, propertyName.c_str()); std::string string = value.isString() ? value.asString(runtime).utf8(runtime) : ""; return string.length() > 0 ? [NSString stringWithUTF8String:string.c_str()] : nil; } RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install:(nullable NSString*)storageDirectory) { NSLog(@"Installing global.mmkvCreateNewInstance..."); //RCTBridge RCTBridge* bridge = [RCTBridge currentBridge]; /// THIS VARIABLE doesnt have access to js call invoker RCTCxxBridge* cxxBridge = (RCTCxxBridge*)bridge; if (cxxBridge == nil) { return @false; } using namespace facebook; auto jsiRuntime = (jsi::Runtime*) cxxBridge.runtime; if (jsiRuntime == nil) { return @false; } auto& runtime = *jsiRuntime; RCTUnsafeExecuteOnMainQueueSync(^{ [MMKV initializeMMKV:storageDirectory]; }); // MMKV.createNewInstance() auto mmkvCreateNewInstance = jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forAscii(runtime, "mmkvCreateNewInstance"), 1, [](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value { if (count != 1) { throw jsi::JSError(runtime, "MMKV.createNewInstance(..) expects one argument (object)!"); } jsi::Object config = arguments[0].asObject(runtime); NSString* instanceId = [MmkvModule getPropertyAsStringOrNilFromObject:config propertyName:"id" runtime:runtime]; NSString* path = [MmkvModule getPropertyAsStringOrNilFromObject:config propertyName:"path" runtime:runtime]; NSString* encryptionKey = [MmkvModule getPropertyAsStringOrNilFromObject:config propertyName:"encryptionKey" runtime:runtime]; auto instance = std::make_shared<MmkvHostObject>(instanceId, path, encryptionKey); return jsi::Object::createFromHostObject(runtime, instance); }); runtime.global().setProperty(runtime, "mmkvCreateNewInstance", std::move(mmkvCreateNewInstance)); NSLog(@"Installed global.mmkvCreateNewInstance!"); return @true; } @end
Xcode报错截图:
解决方案
1. 添加私有头文件依赖
JSCallInvoker的引用在RCTCxxBridge类中,需要显式导入其头文件:
#import <React/RCTCxxBridge.h>
注:私有头文件依赖可能随RN版本迭代发生变化,但在RN 0.66版本中可稳定使用。
2. 从RCTCxxBridge直接获取JSCallInvoker
在你已将RCTBridge强转为RCTCxxBridge的基础上,直接访问jsCallInvoker属性即可:
RCTBridge* bridge = [RCTBridge currentBridge]; RCTCxxBridge* cxxBridge = (RCTCxxBridge*)bridge; if (cxxBridge == nil) { return @false; } // 获取指向JS线程的CallInvoker std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker = cxxBridge.jsCallInvoker;
3. 替代方案:通过TurboModuleManager获取
若私有头文件方式稳定性不足,可通过RCTBridge的turboModuleManager间接获取:
#import <React/RCTTurboModuleManager.h> // ... id<RCTTurboModuleManager> turboModuleManager = bridge.turboModuleManager; std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker = turboModuleManager.jsCallInvoker;
注:此方案需确保项目启用TurboModule,RN 0.66搭配Hermes时默认支持。
4. 修改后的完整代码示例
#import "MmkvModule.h" #import "JSIUtils.h" #import <React/RCTBridge+Private.h> #import <React/RCTCxxBridge.h> // 新增头文件 #import <ReactCommon/CallInvoker.h> #import <React/RCTUtils.h> #import <jsi/jsi.h> #import <MMKV/MMKV.h> #import "MmkvHostObject.h" using namespace facebook; @implementation MmkvModule RCT_EXPORT_MODULE(MMKV) + (NSString*)getPropertyAsStringOrNilFromObject:(jsi::Object&)object propertyName:(std::string)propertyName runtime:(jsi::Runtime&)runtime { jsi::Value value = object.getProperty(runtime, propertyName.c_str()); std::string string = value.isString() ? value.asString(runtime).utf8(runtime) : ""; return string.length() > 0 ? [NSString stringWithUTF8String:string.c_str()] : nil; } RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install:(nullable NSString*)storageDirectory) { NSLog(@"Installing global.mmkvCreateNewInstance..."); RCTBridge* bridge = [RCTBridge currentBridge]; RCTCxxBridge* cxxBridge = (RCTCxxBridge*)bridge; if (cxxBridge == nil) { return @false; } // 获取JSCallInvoker std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker = cxxBridge.jsCallInvoker; if (!jsCallInvoker) { NSLog(@"Failed to retrieve JSCallInvoker"); return @false; } auto jsiRuntime = (jsi::Runtime*) cxxBridge.runtime; if (jsiRuntime == nil) { return @false; } auto& runtime = *jsiRuntime; RCTUnsafeExecuteOnMainQueueSync(^{ [MMKV initializeMMKV:storageDirectory]; }); // MMKV.createNewInstance() auto mmkvCreateNewInstance = jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forAscii(runtime, "mmkvCreateNewInstance"), 1, [jsCallInvoker](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value { if (count != 1) { throw jsi::JSError(runtime, "MMKV.createNewInstance(..) expects one argument (object)!"); } jsi::Object config = arguments[0].asObject(runtime); NSString* instanceId = [MmkvModule getPropertyAsStringOrNilFromObject:config propertyName:"id" runtime:runtime]; NSString* path = [MmkvModule getPropertyAsStringOrNilFromObject:config propertyName:"path" runtime:runtime]; NSString* encryptionKey = [MmkvModule getPropertyAsStringOrNilFromObject:config propertyName:"encryptionKey" runtime:runtime]; auto instance = std::make_shared<MmkvHostObject>(instanceId, path, encryptionKey); return jsi::Object::createFromHostObject(runtime, instance); }); runtime.global().setProperty(runtime, "mmkvCreateNewInstance", std::move(mmkvCreateNewInstance)); NSLog(@"Installed global.mmkvCreateNewInstance!"); return @true; } @end
内容的提问来源于stack exchange,提问作者Nitish Phanse
相关产品推荐
相关产品推荐

