1.基本原理
JS要与原生通信,都依赖于引擎的能力,不论是hermes,还是V8,还是JSCore.向引擎注入额外方法,并实现 JS 调用. 基本思想都是一致的,都是要将 JS 所需的内容设法绑定到 JS 执行环境下的 全局对象 global 上,这样在 JS 侧就可以通过 global 来获取注入的内容了. 实际上是借助于 Js 执行上下文 Context 来实现的。首先需要获取这个 Context 上下文 和 JS global ,然后将 需要注入的 方法 和 变量 设置到 global 当中,最后再将 global 与 Context 进行关联,就完成了向引擎注入的操作。这样就可以在 Js 运行环境中,调用我们注入的方法和变量了。 这里是注入的方法或者变量:
runtime.global().setProperty(
runtime,
"__turboModuleProxy",
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "__turboModuleProxy"),
1,
//$hyrn begin
// [binding =
// TurboModuleBinding(bindingMode, std::move(moduleProvider))](
[binding =
TurboModuleBinding(bindingMode, std::move(moduleProvider), runtime)](
//$hyrn end
jsi::Runtime &rt,
const jsi::Value &thisVal,
const jsi::Value *args,
size_t count) {
if (count < 1) {
throw std::invalid_argument(
"__turboModuleProxy must be called with at least 1 argument");
}
std::string moduleName = args[0].getString(rt).utf8(rt);
return binding.getModule(rt, moduleName);
}));
JS中使用的地方是(TurboModuleRegistry.js文件里面):
const turboModuleProxy = global.__turboModuleProxy;
当然与之对应的,原生也可以直接通过Context操作global中JS的方法或者变量:
m_genNativeModuleJS = rt.global().getPropertyAsFunction(rt, "__fbGenNativeModule");
JS中对应的地方是(NativeModules.js文件里面):
// export this method as a global so we can call it from native
global.__fbGenNativeModule = genModule;
2.NativeModules VS TurboModules
这里是2者的对比图,文章主要不是介绍这里,大家可以看一下.
3.NativeModules实现原理
基本原理是先创建通信bridge的类实例,通过Json传递数据.
先看创建NativeModules原生实现步骤:
1、创建类Module实现协议RCTBridgeModule
2、在.m文件中新增RCT_EXPORT_MODULE()
3、如要导出方法给js用,那么新增:RCT_EXPORT_METHOD
大概代码是:
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
@interface TestModule : NSObject<RCTBridgeModule>
@end
@implementation TestModule
RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(add:(int)a b:(int)b callback:(RCTResponseSenderBlock)callback){
callback(@[ [NSNumber numberWithInt: a+b ] ]);
}
@end
查看RCT_EXPORT_MODULE源码:
#define RCT_EXPORT_MODULE(js_name) \
RCT_EXTERN void RCTRegisterModule(Class); \
+(NSString *)moduleName \
{ \
return @ #js_name; \
} \
+(void)load \
{ \
RCTRegisterModule(self); \
}
RCTRegisterModule
void RCTRegisterModule(Class);
void RCTRegisterModule(Class moduleClass)
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
RCTModuleClasses = [NSMutableArray new];
RCTModuleClassesSyncQueue =
dispatch_queue_create("com.facebook.react.ModuleClassesSyncQueue", DISPATCH_QUEUE_CONCURRENT);
});
RCTAssert(
[moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
@"%@ does not conform to the RCTBridgeModule protocol",
moduleClass);
// Register module
dispatch_barrier_async(RCTModuleClassesSyncQueue, ^{
[RCTModuleClasses addObject:moduleClass];
});
}
主要作用是在+load阶段,将moduleClass记录在RCTModuleClasses数组中.
然后我们看下这个RCTModuleClasses在哪里被使用的呢?是在RCTCxxBridge下面的代码使用的:
- (void)start
{
......
[self registerExtraModules];
// Initialize all native modules that cannot be loaded lazily
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
[self registerExtraLazyModules];
......
}
那这个start是在哪里调用的呢?看堆栈是在bridge创建的时候调用的
接下来看_initializeModules:具体干了啥?
- (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<Class> *)modules
withDispatchGroup:(dispatch_group_t)dispatchGroup
lazilyDiscovered:(BOOL)lazilyDiscovered
{
// Set up moduleData for automatically-exported modules
NSArray<RCTModuleData *> *moduleDataById = [self _registerModulesForClasses:modules
lazilyDiscovered:lazilyDiscovered];
......
for (RCTModuleData *moduleData in _moduleDataByID) {
if (moduleData.hasInstance && (!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) {
// Modules that were pre-initialized should ideally be set up before
// bridge init has finished, otherwise the caller may try to access the
// module directly rather than via `[bridge moduleForClass:]`, which won't
// trigger the lazy initialization process. If the module cannot safely be
// set up on the current thread, it will instead be async dispatched
// to the main thread to be set up in _prepareModulesWithDispatchGroup:.
(void)[moduleData instance];
}
}
......
return moduleDataById;
}
调用了_registerModulesForClasses方法,将moduleName和moduleDataByID和RCTModuleData等信息通过属性的形式保存在bridge中.
- (NSArray<RCTModuleData *> *)_registerModulesForClasses:(NSArray<Class> *)moduleClasses
lazilyDiscovered:(BOOL)lazilyDiscovered
{
RCT_PROFILE_BEGIN_EVENT(
RCTProfileTagAlways, @"-[RCTCxxBridge initModulesWithDispatchGroup:] autoexported moduleData", nil);
NSArray *moduleClassesCopy = [moduleClasses copy];
NSMutableArray<RCTModuleData *> *moduleDataByID = [NSMutableArray arrayWithCapacity:moduleClassesCopy.count];
for (Class moduleClass in moduleClassesCopy) {
//$hyrn begin
if (RCTTurboModuleEnabled() && [moduleClass conformsToProtocol:@protocol(RCTTurboModule)] && moduleClass == NSClassFromString(@"HYDataCenterV2")) {
// if (RCTTurboModuleEnabled() && [moduleClass conformsToProtocol:@protocol(RCTTurboModule)]) {
//$hyrn end
continue;
}
NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
// Check for module name collisions
RCTModuleData *moduleData = _moduleDataByName[moduleName];
if (moduleData) {
if (moduleData.hasInstance || lazilyDiscovered) {
// Existing module was preregistered, so it takes precedence
continue;
} else if ([moduleClass new] == nil) {
// The new module returned nil from init, so use the old module
continue;
} else if ([moduleData.moduleClass new] != nil) {
// Both modules were non-nil, so it's unclear which should take precedence
RCTLogWarn(
@"Attempted to register RCTBridgeModule class %@ for the "
"name '%@', but name was already registered by class %@",
moduleClass,
moduleName,
moduleData.moduleClass);
}
}
// Instantiate moduleData
// TODO #13258411: can we defer this until config generation?
int32_t moduleDataId = getUniqueId();
BridgeNativeModulePerfLogger::moduleDataCreateStart([moduleName UTF8String], moduleDataId);
moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass
bridge:self
moduleRegistry:_objCModuleRegistry
viewRegistry_DEPRECATED:_viewRegistry_DEPRECATED
bundleManager:_bundleManager
callableJSModules:_callableJSModules];
BridgeNativeModulePerfLogger::moduleDataCreateEnd([moduleName UTF8String], moduleDataId);
_moduleDataByName[moduleName] = moduleData;
[_moduleClassesByID addObject:moduleClass];
[moduleDataByID addObject:moduleData];
}
[_moduleDataByID addObjectsFromArray:moduleDataByID];
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
return moduleDataByID;
}
然后调用每个[RCTModuleData instance]方法,最后返回一个RCTModuleData对象的列表.在instance里面就会把具体的NativeModules bridge类实例化.具体可以自己断点看下.
那这些信息是怎么被JS知道的呢?我们继续往下看start方法中_initializeBridge:的调用,最终会调到下面这个方法:
void Instance::initializeBridge(
std::unique_ptr<InstanceCallback> callback,
std::shared_ptr<JSExecutorFactory> jsef,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<ModuleRegistry> moduleRegistry) {
callback_ = std::move(callback);
moduleRegistry_ = std::move(moduleRegistry);
jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
nativeToJsBridge_ = std::make_shared<NativeToJsBridge>(
jsef.get(), moduleRegistry_, jsQueue, callback_);
nativeToJsBridge_->initializeRuntime();
/**
* After NativeToJsBridge is created, the jsi::Runtime should exist.
* Also, the JS message queue thread exists. So, it's safe to
* schedule all queued up js Calls.
*/
jsCallInvoker_->setNativeToJsBridgeAndFlushCalls(nativeToJsBridge_);
std::lock_guard<std::mutex> lock(m_syncMutex);
m_syncReady = true;
m_syncCV.notify_all();
});
CHECK(nativeToJsBridge_);
}
我们来看看里面关键的的代码:
1.ModuleRegistry
moduleRegistry 中包括了所有 native module 信息,即 RCTModuleData;他是取上面保存的_moduleDataByID的信息保存到ModuleRegistry类,具体代码如下:
- (std::shared_ptr<ModuleRegistry>)_buildModuleRegistryUnlocked
{
if (!self.valid) {
return {};
}
[_performanceLogger markStartForTag:RCTPLNativeModulePrepareConfig];
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge buildModuleRegistry]", nil);
__weak __typeof(self) weakSelf = self;
ModuleRegistry::ModuleNotFoundCallback moduleNotFoundCallback = ^bool(const std::string &name) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
return [strongSelf.delegate respondsToSelector:@selector(bridge:didNotFindModule:)] &&
[strongSelf.delegate bridge:strongSelf didNotFindModule:@(name.c_str())];
};
auto registry = std::make_shared<ModuleRegistry>(
createNativeModules(_moduleDataByID, self, _reactInstance), moduleNotFoundCallback);
[_performanceLogger markStopForTag:RCTPLNativeModulePrepareConfig];
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
return registry;
}
2.NativeToJsBridge 的初始化
下面我们就来具体 NativeToJsBridge 是干嘛的
NativeToJsBridge::NativeToJsBridge(
JSExecutorFactory *jsExecutorFactory,
std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<InstanceCallback> callback)
: m_destroyed(std::make_shared<bool>(false)),
m_delegate(std::make_shared<JsToNativeBridge>(registry, callback)),
m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)),
m_executorMessageQueueThread(std::move(jsQueue)),
m_inspectable(m_executor->isInspectable()) {}
具体作用如下
1.其中 registry 和 callback 作为入参生成了一个 JsToNativeBridge 类型实例赋值给 m_delegate
2.使用 JSExecutorFactory 和 _delegate 创建了一个 JSExecutor;
3.从 RCTxxBridge 中创建的JSExecutorFactory得知此 JSExecutor 就是 JSIExecutor
JSIExecutor
前面我们已经将 RCTBridge 的初始化都讲解完成,还剩余最后一个部分,就是 JSIExecutor; 从前面的部分我们知道 NativeToJsBridge持有 JSIExecutor 的实例.我门来看看JSIExecutor具体是干嘛的?
JSIExecutor::JSIExecutor(
std::shared_ptr<jsi::Runtime> runtime,
std::shared_ptr<ExecutorDelegate> delegate,
const JSIScopedTimeoutInvoker &scopedTimeoutInvoker,
RuntimeInstaller runtimeInstaller)
: runtime_(runtime),
delegate_(delegate),
nativeModules_(std::make_shared<JSINativeModules>(
delegate ? delegate->getModuleRegistry() : nullptr)),
moduleRegistry_(delegate ? delegate->getModuleRegistry() : nullptr),
scopedTimeoutInvoker_(scopedTimeoutInvoker),
runtimeInstaller_(runtimeInstaller) {
runtime_->global().setProperty(
*runtime, "__jsiExecutorDescription", runtime->description());
}
我们看下nativeModules_,这是一个JSINativeModules类.JSINativeModules 由上层传入的 ModuleRegistry 构造而成;而 ModuleRegistry 又是在 RCTxxBridge 中构造而成。前面的代码中,有调用
void JSIExecutor::initializeRuntime() {
SystraceSection s("JSIExecutor::initializeRuntime");
runtime_->global().setProperty(
*runtime_,
"nativeModuleProxy",
Object::createFromHostObject(
*runtime_, std::make_shared<NativeModuleProxy>(nativeModules_)));
runtime_->global().setProperty(
*runtime_,
"nativeFlushQueueImmediate",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) {
if (count != 1) {
throw std::invalid_argument(
"nativeFlushQueueImmediate arg count must be 1");
}
callNativeModules(args[0], false);
return Value::undefined();
}));
runtime_->global().setProperty(
*runtime_,
"nativeCallSyncHook",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeCallSyncHook"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) { return nativeCallSyncHook(args, count); }));
runtime_->global().setProperty(
*runtime_,
"globalEvalWithSourceUrl",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "globalEvalWithSourceUrl"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) { return globalEvalWithSourceUrl(args, count); }));
if (runtimeInstaller_) {
runtimeInstaller_(*runtime_);
}
bool hasLogger(ReactMarker::logTaggedMarkerImpl);
if (hasLogger) {
ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);
}
}
里面有个注入nativeModuleProxy,对应RN NativeModule.js里
let NativeModules: {[moduleName: string]: $FlowFixMe, ...} = {};
if (global.nativeModuleProxy) {
NativeModules = global.nativeModuleProxy;
最终会调到下面的方法:
Value get(Runtime &rt, const PropNameID &name) override {
if (name.utf8(rt) == "name") {
return jsi::String::createFromAscii(rt, "NativeModules");
}
auto nativeModules = weakNativeModules_.lock();
if (!nativeModules) {
return nullptr;
}
return nativeModules->getModule(rt, name);
}
getModule会调下面方法:
folly::Optional<Object> JSINativeModules::createModule(
//$hyrn end
Runtime &rt,
const std::string &name) {
bool hasLogger(ReactMarker::logTaggedMarkerImpl);
if (hasLogger) {
ReactMarker::logTaggedMarker(
ReactMarker::NATIVE_MODULE_SETUP_START, name.c_str());
}
if (!m_genNativeModuleJS) {
m_genNativeModuleJS =
rt.global().getPropertyAsFunction(rt, "__fbGenNativeModule");
}
auto result = m_moduleRegistry->getConfig(name);
if (!result.has_value()) {
//$hyrn begin
// return std::nullopt;
return folly::none;
//$hyrn end
}
Value moduleInfo = m_genNativeModuleJS->call(
rt,
valueFromDynamic(rt, result->config),
static_cast<double>(result->index));
CHECK(!moduleInfo.isNull()) << "Module returned from genNativeModule is null";
CHECK(moduleInfo.isObject())
<< "Module returned from genNativeModule isn't an Object";
//$hyrn begin
// std::optional<Object> module(
// moduleInfo.asObject(rt).getPropertyAsObject(rt, "module"));
folly::Optional<Object> module(
moduleInfo.asObject(rt).getPropertyAsObject(rt, "module"));
//$hyrn end
if (hasLogger) {
ReactMarker::logTaggedMarker(
ReactMarker::NATIVE_MODULE_SETUP_STOP, name.c_str());
}
return module;
}
上面这个方法有个__fbGenNativeModule,实际调用的是JS的genModule方法:
function genModule(
config: ?ModuleConfig,
moduleID: number,
): ?{
name: string,
module?: {...},
...
} {
if (!config) {
return null;
}
const [moduleName, constants, methods, promiseMethods, syncMethods] = config;
invariant(
!moduleName.startsWith('RCT') && !moduleName.startsWith('RK'),
"Module name prefixes should've been stripped by the native side " +
"but wasn't for " +
moduleName,
);
if (!constants && !methods) {
// Module contents will be filled in lazily later
return {name: moduleName};
}
const module: {[string]: mixed} = {};
methods &&
methods.forEach((methodName, methodID) => {
const isPromise =
(promiseMethods && arrayContains(promiseMethods, methodID)) || false;
const isSync =
(syncMethods && arrayContains(syncMethods, methodID)) || false;
invariant(
!isPromise || !isSync,
'Cannot have a method that is both async and a sync hook',
);
const methodType = isPromise ? 'promise' : isSync ? 'sync' : 'async';
module[methodName] = genMethod(moduleID, methodID, methodType);
});
Object.assign(module, constants);
if (module.getConstants == null) {
module.getConstants = () => constants || Object.freeze({});
} else {
console.warn(
`Unable to define method 'getConstants()' on NativeModule '${moduleName}'. NativeModule '${moduleName}' already has a constant or method called 'getConstants'. Please remove it.`,
);
}
if (__DEV__) {
BatchedBridge.createDebugLookup(moduleID, moduleName, methods);
}
return {name: moduleName, module};
}
这里JS中就获取到原生moduleName和方法信息,并对他做了对应的映射,所以JS调用了NativeModule最终获取到{name: moduleName, module},也就是有原生全部的信息了.至于调用具体的方法,对应的JS方法是下面的:
function genMethod(moduleID: number, methodID: number, type: MethodType) {
let fn = null;
if (type === 'promise') {
fn = function promiseMethodWrapper(...args: Array<mixed>) {
// In case we reject, capture a useful stack trace here.
/* $FlowFixMe[class-object-subtyping] added when improving typing for
* this parameters */
const enqueueingFrameError: ExtendedError = new Error();
return new Promise((resolve, reject) => {
BatchedBridge.enqueueNativeCall(
moduleID,
methodID,
args,
data => resolve(data),
errorData =>
reject(
updateErrorWithErrorData(
(errorData: $FlowFixMe),
enqueueingFrameError,
),
),
);
});
};
} else {
fn = function nonPromiseMethodWrapper(...args: Array<mixed>) {
const lastArg = args.length > 0 ? args[args.length - 1] : null;
const secondLastArg = args.length > 1 ? args[args.length - 2] : null;
const hasSuccessCallback = typeof lastArg === 'function';
const hasErrorCallback = typeof secondLastArg === 'function';
hasErrorCallback &&
invariant(
hasSuccessCallback,
'Cannot have a non-function arg after a function arg.',
);
// $FlowFixMe[incompatible-type]
const onSuccess: ?(mixed) => void = hasSuccessCallback ? lastArg : null;
// $FlowFixMe[incompatible-type]
const onFail: ?(mixed) => void = hasErrorCallback ? secondLastArg : null;
// $FlowFixMe[unsafe-addition]
const callbackCount = hasSuccessCallback + hasErrorCallback;
const newArgs = args.slice(0, args.length - callbackCount);
if (type === 'sync') {
return BatchedBridge.callNativeSyncHook(
moduleID,
methodID,
newArgs,
onFail,
onSuccess,
);
} else {
BatchedBridge.enqueueNativeCall(
moduleID,
methodID,
newArgs,
onFail,
onSuccess,
);
}
};
}
// $FlowFixMe[prop-missing]
fn.type = type;
return fn;
}
enqueueNativeCall,callNativeSyncHook,enqueueNativeCall是不是很眼熟,就是之前initializeRuntime另外几个注入的信息,真正会调原生nativeCallSyncHook,或者nativeFlushQueueImmediate注入的方法.另外异步函数调用解析对应json的代码是在下面代码这里parseMethodCalls,大家可以看下实现.所以NativeModules是以json为数据结构通信的.最终moduleId和methodid找到对应的方法,动态调用对应的原生接口代码,具体看下m_registry->callNativeMethod.
void callNativeModules(
[[maybe_unused]] JSExecutor &executor,
folly::dynamic &&calls,
bool isEndOfBatch) override {
CHECK(m_registry || calls.empty())
<< "native module calls cannot be completed with no native modules";
m_batchHadNativeModuleOrTurboModuleCalls =
m_batchHadNativeModuleOrTurboModuleCalls || !calls.empty();
std::vector<MethodCall> methodCalls = parseMethodCalls(std::move(calls));
BridgeNativeModulePerfLogger::asyncMethodCallBatchPreprocessEnd(
(int)methodCalls.size());
// An exception anywhere in here stops processing of the batch. This
// was the behavior of the Android bridge, and since exception handling
// terminates the whole bridge, there's not much point in continuing.
for (auto &call : methodCalls) {
m_registry->callNativeMethod(
call.moduleId, call.methodId, std::move(call.arguments), call.callId);
}
if (isEndOfBatch) {
// onBatchComplete will be called on the native (module) queue, but
// decrementPendingJSCalls will be called sync. Be aware that the bridge
// may still be processing native calls when the bridge idle signaler
// fires.
if (m_batchHadNativeModuleOrTurboModuleCalls) {
m_callback->onBatchComplete();
m_batchHadNativeModuleOrTurboModuleCalls = false;
}
m_callback->decrementPendingJSCalls();
}
}
4.TurboModules实现原理
上面说是NativeModules的,如果换成TurboModules,就会在RCTCxxBridge start中执行RCTTurboModuleManager中的下面这个方法:
- (void)installJSBindingWithRuntimeExecutor:(facebook::react::RuntimeExecutor &)runtimeExecutor
{
if (!runtimeExecutor) {
// jsi::Runtime doesn't exist when attached to Chrome debugger.
return;
}
/**
* We keep TurboModuleManager alive until the JS VM is deleted.
* It is perfectly valid to only use/create TurboModules from JS.
* In such a case, we shouldn't dealloc TurboModuleManager if there
* aren't any strong references to it in ObjC. Hence, we give
* __**turboModuleProxy a strong reference to TurboModuleManager.**
*/
auto turboModuleProvider = [self](const std::string &name) -> std::shared_ptr<react::TurboModule> {
auto moduleName = name.c_str();
TurboModulePerfLogger::moduleJSRequireBeginningStart(moduleName);
auto moduleWasNotInitialized = ![self moduleIsInitialized:moduleName];
if (moduleWasNotInitialized) {
[self->_bridge.performanceLogger markStartForTag:RCTPLTurboModuleSetup];
}
/**
* By default, all TurboModules are long-lived.
* Additionally, if a TurboModule with the name `name` isn't found, then we
* trigger an assertion failure.
*/
auto turboModule = [self provideTurboModule:moduleName];
if (moduleWasNotInitialized && [self moduleIsInitialized:moduleName]) {
[self->_bridge.performanceLogger markStopForTag:RCTPLTurboModuleSetup];
[self notifyAboutTurboModuleSetup:moduleName];
}
if (turboModule) {
TurboModulePerfLogger::moduleJSRequireEndingEnd(moduleName);
} else {
TurboModulePerfLogger::moduleJSRequireEndingFail(moduleName);
}
return turboModule;
};
runtimeExecutor([turboModuleProvider = std::move(turboModuleProvider)](jsi::Runtime &runtime) {
TurboModuleBinding::install(runtime, sTurboModuleBindingMode, std::move(turboModuleProvider));
});
}
方法有个turboModuleProvider,类似于block,再用这个作为参数调用TurboModuleBinding::install方法,如下:
void TurboModuleBinding::install(
jsi::Runtime &runtime,
TurboModuleBindingMode bindingMode,
TurboModuleProviderFunctionType &&moduleProvider) {
runtime.global().setProperty(
runtime,
"__turboModuleProxy",
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "__turboModuleProxy"),
1,
//$hyrn begin
// [binding =
// TurboModuleBinding(bindingMode, std::move(moduleProvider))](
[binding =
TurboModuleBinding(bindingMode, std::move(moduleProvider), runtime)](
//$hyrn end
jsi::Runtime &rt,
const jsi::Value &thisVal,
const jsi::Value *args,
size_t count) {
if (count < 1) {
throw std::invalid_argument(
"__turboModuleProxy must be called with at least 1 argument");
}
std::string moduleName = args[0].getString(rt).utf8(rt);
return binding.getModule(rt, moduleName);
}));
}
这个方法往JS注入一个属性__turboModuleProxy,对应JS代码是TurboModuleRegistry.js文件里的:
const turboModuleProxy = global.__turboModuleProxy;
最终返回的就是上面的turboModuleProvider的返回值.我们看看这个方法具体干了什么,最终断点到下面代码:
- (id<RCTTurboModule>)_provideRCTTurboModule:(const char *)moduleName
moduleHolder:(TurboModuleHolder *)moduleHolder
shouldPerfLog:(BOOL)shouldPerfLog
{
bool shouldCreateModule = false;
{
std::lock_guard<std::mutex> guard(moduleHolder->mutex());
if (moduleHolder->isDoneCreatingModule()) {
if (shouldPerfLog) {
TurboModulePerfLogger::moduleCreateCacheHit(moduleName, moduleHolder->getModuleId());
}
return moduleHolder->getModule();
}
if (!moduleHolder->isCreatingModule()) {
shouldCreateModule = true;
moduleHolder->startCreatingModule();
}
}
if (shouldCreateModule) {
Class moduleClass;
/**
* Step 2a: Resolve platform-specific class.
*/
if ([_delegate respondsToSelector:@selector(getModuleClassFromName:)]) {
if (RCTTurboModuleManagerDelegateLockingDisabled()) {
moduleClass = [_delegate getModuleClassFromName:moduleName];
} else {
std::lock_guard<std::mutex> delegateGuard(_turboModuleManagerDelegateMutex);
moduleClass = [_delegate getModuleClassFromName:moduleName];
}
}
if (!moduleClass) {
moduleClass = getFallbackClassFromName(moduleName);
}
__block id<RCTTurboModule> module = nil;
if ([moduleClass conformsToProtocol:@protocol(RCTTurboModule)]) {
__weak __typeof(self) weakSelf = self;
dispatch_block_t work = ^{
auto strongSelf = weakSelf;
if (!strongSelf) {
return;
}
module = [strongSelf _createAndSetUpRCTTurboModule:moduleClass
moduleName:moduleName
moduleId:moduleHolder->getModuleId()];
};
if ([self _requiresMainQueueSetup:moduleClass]) {
/**
* When TurboModule eager initialization is enabled, there shouldn't be any TurboModule initializations on the
* main queue.
* TODO(T69449176) Roll out TurboModule eager initialization, and remove this check.
*/
if (RCTTurboModuleEagerInitEnabled() && !RCTIsMainQueue()) {
RCTLogWarn(
@"TurboModule \"%@\" requires synchronous dispatch onto the main queue to be initialized. This may lead to deadlock.",
moduleClass);
}
RCTUnsafeExecuteOnMainQueueSync(work);
} else {
work();
}
}
{
std::lock_guard<std::mutex> guard(moduleHolder->mutex());
moduleHolder->setModule(module);
moduleHolder->endCreatingModule();
}
moduleHolder->cv().notify_all();
return module;
}
std::unique_lock<std::mutex> guard(moduleHolder->mutex());
while (moduleHolder->isCreatingModule()) {
/**
* TODO(T65905574):
* If the thread responsible for creating and initializing the NativeModule stalls, we'll wait here indefinitely.
* This is the behaviour in legacy NativeModules. Changing this now could lead to more crashes/problems in
* TurboModules than in NativeModules, which'll make it more difficult to test the TurboModules infra. Therefore,
* we should consider making it post TurboModule 100% rollout.
*/
moduleHolder->cv().wait(guard);
}
return moduleHolder->getModule();
}
这个方法是获取Class,调用下面的方法:
- (id<RCTTurboModule>)_createAndSetUpRCTTurboModule:(Class)moduleClass
moduleName:(const char *)moduleName
moduleId:(int32_t)moduleId
{
id<RCTTurboModule> module = nil;
/**
* Step 2b: Ask hosting application/delegate to instantiate this class
*/
TurboModulePerfLogger::moduleCreateConstructStart(moduleName, moduleId);
if ([_delegate respondsToSelector:@selector(getModuleInstanceFromClass:)]) {
if (RCTTurboModuleManagerDelegateLockingDisabled()) {
module = [_delegate getModuleInstanceFromClass:moduleClass];
} else {
std::lock_guard<std::mutex> delegateGuard(_turboModuleManagerDelegateMutex);
module = [_delegate getModuleInstanceFromClass:moduleClass];
}
/**
* If the application is unable to create the TurboModule object from its class:
* abort TurboModule creation, and early return nil.
*/
if (!module) {
RCTLogError(
@"TurboModuleManager delegate %@ returned nil TurboModule object for module with name=\"%s\" and class=%@",
NSStringFromClass([_delegate class]),
moduleName,
NSStringFromClass(moduleClass));
return nil;
}
} else {
module = [moduleClass new];
}
TurboModulePerfLogger::moduleCreateConstructEnd(moduleName, moduleId);
TurboModulePerfLogger::moduleCreateSetUpStart(moduleName, moduleId);
/**
* It is reasonable for NativeModules to not want/need the bridge.
* In such cases, they won't have `@synthesize bridge = _bridge` in their
* implementation, and a `- (RCTBridge *) bridge { ... }` method won't be
* generated by the ObjC runtime. The property will also not be backed
* by an ivar, which makes writing to it unsafe. Therefore, we check if
* this method exists to know if we can safely set the bridge to the
* NativeModule.
*/
if ([module respondsToSelector:@selector(bridge)] && _bridge) {
/**
* Just because a NativeModule has the `bridge` method, it doesn't mean
* that it has synthesized the bridge in its implementation. Therefore,
* we need to surround the code that sets the bridge to the NativeModule
* inside a try/catch. This catches the cases where the NativeModule
* author specifies a `bridge` method manually.
*/
@try {
/**
* RCTBridgeModule declares the bridge property as readonly.
* Therefore, when authors of NativeModules synthesize the bridge
* via @synthesize bridge = bridge;, the ObjC runtime generates
* only a - (RCTBridge * *) bridge: { ... } method. No setter is*
* *generated, so we have have to rely on the KVC API of ObjC to set*
* *the bridge property of these NativeModules.*
*/
[(id)module setValue:_bridge forKey:@"bridge"];
} @catch (NSException *exception) {
RCTLogError(
@"%@ has no setter or ivar for its bridge, which is not "
"permitted. You must either @synthesize the bridge property, "
"or provide your own setter method.",
RCTBridgeModuleNameForClass([module class]));
}
}
/**
* Some modules need their own queues, but don't provide any, so we need to create it for them.
* These modules typically have the following:
* `@synthesize methodQueue = _methodQueue`
*/
dispatch_queue_t methodQueue = nil;
BOOL moduleHasMethodQueueGetter = [module respondsToSelector:@selector(methodQueue)];
if (moduleHasMethodQueueGetter) {
methodQueue = [(id<RCTBridgeModule>)module methodQueue];
}
/**
* Note: RCTJSThread, which is a valid method queue, is defined as (id)kCFNull. It should rightfully not enter the
* following if condition's block.
*/
if (!methodQueue) {
NSString *methodQueueName = [NSString stringWithFormat:@"com.facebook.react.%sQueue", moduleName];
methodQueue = dispatch_queue_create(methodQueueName.UTF8String, DISPATCH_QUEUE_SERIAL);
if (moduleHasMethodQueueGetter) {
/**
* If the module has a method queue getter, two cases are possible:
* - We @synthesized the method queue. In this case, the getter will initially return nil.
* - We had a custom methodQueue function on the NativeModule. If we got this far, then that getter returned
* nil.
*
* Therefore, we do a try/catch and use ObjC's KVC API and try to assign the method queue to the NativeModule.
* In case 1, we'll succeed. In case 2, an exception will be thrown, which we'll ignore.
*/
@try {
[(id)module setValue:methodQueue forKey:@"methodQueue"];
} @catch (NSException *exception) {
RCTLogError(
@"%@ has no setter or ivar for its methodQueue, which is not "
"permitted. You must either @synthesize the methodQueue property, "
"or provide your own setter method.",
RCTBridgeModuleNameForClass([module class]));
}
}
}
/**
* Decorate TurboModules with bridgeless-compatible APIs that call into the bridge.
*/
if (_bridge) {
[_bridge attachBridgeAPIsToTurboModule:module];
}
/**
* If the TurboModule conforms to RCTInitializing, invoke its initialize method.
*/
if ([module respondsToSelector:@selector(initialize)]) {
[(id<RCTInitializing>)module initialize];
}
/**
* Attach method queue to id<RCTTurboModule> object.
* This is necessary because the id<RCTTurboModule> object can be eagerly created/initialized before the method
* queue is required. The method queue is required for an id<RCTTurboModule> for JS -> Native calls. So, we need it
* before we create the id<RCTTurboModule>'s TurboModule jsi::HostObject in provideTurboModule:.
*/
objc_setAssociatedObject(module, &kAssociatedMethodQueueKey, methodQueue, OBJC_ASSOCIATION_RETAIN);
/**
* NativeModules that implement the RCTFrameUpdateObserver protocol
* require registration with RCTDisplayLink.
*
* TODO(T55504345): Investigate whether we can improve this after TM
* rollout.
*/
if (_bridge) {
RCTModuleData *data = [[RCTModuleData alloc] initWithModuleInstance:(id<RCTBridgeModule>)module
bridge:_bridge
moduleRegistry:_bridge.moduleRegistry
viewRegistry_DEPRECATED:nil
bundleManager:nil
callableJSModules:nil];
[_bridge registerModuleForFrameUpdates:(id<RCTBridgeModule>)module withModuleData:data];
}
/**
* Broadcast that this TurboModule was created.
*
* TODO(T41180176): Investigate whether we can delete this after TM
* rollout.
*/
[[NSNotificationCenter defaultCenter]
postNotificationName:RCTDidInitializeModuleNotification
object:_bridge
userInfo:@{@"module" : module, @"bridge" : RCTNullIfNil([_bridge parentBridge])}];
TurboModulePerfLogger::moduleCreateSetUpEnd(moduleName, moduleId);
return module;
}
里面会把一些信息通过registerModuleForFrameUpdates:注册到bridge中去,并通过上面的Class创建一个id的对象返回,而这个对象更好是是我们创建需要继承的协议
@protocol RCTTurboModule <NSObject>
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params;
@end
而这个协议必须实现getTurboModule,他返回一个TurboModule对象,这个继承自HostObject,而前面turboModuleProvider正是获取这个返回.也就是JS中调用TurboModuleRegistry.get('xxx'),实际上是放回一个HostObject对象.然后我们看下TurboModule的get方法:
facebook::jsi::Value get(
facebook::jsi::Runtime &runtime,
const facebook::jsi::PropNameID &propName) override {
{
std::string propNameUtf8 = propName.utf8(runtime);
auto p = methodMap_.find(propNameUtf8);
if (p == methodMap_.end()) {
// Method was not found, let JS decide what to do.
return facebook::jsi::Value::undefined();
} else {
auto moduleMethod = createHostFunction(runtime, propName, p->second);
// If we have a JS wrapper, cache the result of this lookup
// We don't cache misses, to allow for methodMap_ to dynamically be
// extended
if (jsRepresentation_) {
jsRepresentation_->lock(runtime).asObject(runtime).setProperty(
runtime, propName, moduleMethod);
}
return moduleMethod;
}
}
}
上面的方法,通过methodMap_创建HostFunction,并通过setProperty设置给JS调用.而methodMap_是在
NativeTestSpecJSI::NativeTestSpecJSI(const ObjCTurboModule::InitParams ¶ms)
: ObjCTurboModule(params {
// __hostFunction_NativeTurboTestSpecJSI_add详见3.5.1
methodMap_["add"] = MethodMetadata {2, __hostFunction_NativeTurboTestSpecJSI_add};
}
设置的,所以实际执行的是__hostFunction_NativeTurboTestSpecJSI_add,这个方法是:
static facebook::jsi::Value __hostFunction_NativeTurboTestSpecJSI_add(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
return static_cast<ObjCTurboModule &>(turboModule).invokeObjCMethod(rt, PromiseKind, "test", @selector(test:resolve:reject:), args, count);
}
最终通过runtime调用到具体方法,实现是:
jsi::Value ObjCTurboModule::invokeObjCMethod(
jsi::Runtime &runtime,
TurboModuleMethodValueKind returnType,
const std::string &methodNameStr,
SEL selector,
const jsi::Value *args,
size_t count)
{
const char *moduleName = name_.c_str();
const char *methodName = methodNameStr.c_str();
if (isMethodSync(returnType)) {
TurboModulePerfLogger::syncMethodCallStart(moduleName, methodName);
} else {
TurboModulePerfLogger::asyncMethodCallStart(moduleName, methodName);
}
NSMutableArray *retainedObjectsForInvocation = [NSMutableArray arrayWithCapacity:count + 2];
NSInvocation *inv =
getMethodInvocation(runtime, returnType, methodName, selector, args, count, retainedObjectsForInvocation);
jsi::Value returnValue = returnType == PromiseKind
? createPromise(
runtime,
methodNameStr,
^(RCTPromiseResolveBlock resolveBlock, RCTPromiseRejectBlock rejectBlock) {
RCTPromiseResolveBlock resolveCopy = [resolveBlock copy];
RCTPromiseRejectBlock rejectCopy = [rejectBlock copy];
[inv setArgument:(void *)&resolveCopy atIndex:count + 2];
[inv setArgument:(void *)&rejectCopy atIndex:count + 3];
[retainedObjectsForInvocation addObject:resolveCopy];
[retainedObjectsForInvocation addObject:rejectCopy];
// The return type becomes void in the ObjC side.
performMethodInvocation(runtime, VoidKind, methodName, inv, retainedObjectsForInvocation);
})
: performMethodInvocation(runtime, returnType, methodName, inv, retainedObjectsForInvocation);
if (isMethodSync(returnType)) {
TurboModulePerfLogger::syncMethodCallEnd(moduleName, methodName);
} else {
TurboModulePerfLogger::asyncMethodCallEnd(moduleName, methodName);
}
return returnValue;
}
刚看了下源码,有问题欢迎大家指证.中间也找了些文章粘贴复制,希望大家不要介意.