Customize main thread initialization of native modules
Reviewed By: mhorowitz Differential Revision: D3580025 fbshipit-source-id: 18abc15b894c745f584d91da47035c6db036aafa
This commit is contained in:
parent
6ae348e8de
commit
47d6d289d4
|
@ -16,8 +16,9 @@ const NativeModules = require('NativeModules');
|
||||||
const { UIManager } = NativeModules;
|
const { UIManager } = NativeModules;
|
||||||
|
|
||||||
const findNodeHandle = require('react/lib/findNodeHandle');
|
const findNodeHandle = require('react/lib/findNodeHandle');
|
||||||
|
const invariant = require('fbjs/lib/invariant');
|
||||||
|
|
||||||
const _takeSnapshot = UIManager.takeSnapshot;
|
invariant(UIManager, 'UIManager is undefined. The native module config is probably incorrect.');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Capture an image of the screen, window or an individual view. The image
|
* Capture an image of the screen, window or an individual view. The image
|
||||||
|
@ -39,12 +40,13 @@ const _takeSnapshot = UIManager.takeSnapshot;
|
||||||
UIManager.takeSnapshot = async function(
|
UIManager.takeSnapshot = async function(
|
||||||
view ?: 'window' | ReactElement<any> | number,
|
view ?: 'window' | ReactElement<any> | number,
|
||||||
options ?: {
|
options ?: {
|
||||||
width ?: number;
|
width ?: number,
|
||||||
height ?: number;
|
height ?: number,
|
||||||
format ?: 'png' | 'jpeg';
|
format ?: 'png' | 'jpeg',
|
||||||
quality ?: number;
|
quality ?: number,
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
const _takeSnapshot = UIManager.takeSnapshot;
|
||||||
if (!_takeSnapshot) {
|
if (!_takeSnapshot) {
|
||||||
console.warn('UIManager.takeSnapshot is not available on this platform');
|
console.warn('UIManager.takeSnapshot is not available on this platform');
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -46,9 +46,10 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
|
||||||
{
|
{
|
||||||
BOOL _wasBatchActive;
|
BOOL _wasBatchActive;
|
||||||
NSMutableArray<dispatch_block_t> *_pendingCalls;
|
NSMutableArray<dispatch_block_t> *_pendingCalls;
|
||||||
NSMutableDictionary<NSString *, RCTModuleData *> *_moduleDataByName;
|
NSDictionary<NSString *, RCTModuleData *> *_moduleDataByName;
|
||||||
NSArray<RCTModuleData *> *_moduleDataByID;
|
NSArray<RCTModuleData *> *_moduleDataByID;
|
||||||
NSArray<Class> *_moduleClassesByID;
|
NSArray<Class> *_moduleClassesByID;
|
||||||
|
NSUInteger _modulesInitializedOnMainQueue;
|
||||||
RCTDisplayLink *_displayLink;
|
RCTDisplayLink *_displayLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +100,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)dele
|
||||||
|
|
||||||
- (void)start
|
- (void)start
|
||||||
{
|
{
|
||||||
|
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBatchedBridge setUp]", nil);
|
||||||
|
|
||||||
dispatch_queue_t bridgeQueue = dispatch_queue_create("com.facebook.react.RCTBridgeQueue", DISPATCH_QUEUE_CONCURRENT);
|
dispatch_queue_t bridgeQueue = dispatch_queue_create("com.facebook.react.RCTBridgeQueue", DISPATCH_QUEUE_CONCURRENT);
|
||||||
|
|
||||||
dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
|
dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
|
||||||
|
@ -137,9 +140,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)dele
|
||||||
// Asynchronously gather the module config
|
// Asynchronously gather the module config
|
||||||
dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
|
dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
|
||||||
if (weakSelf.valid) {
|
if (weakSelf.valid) {
|
||||||
|
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBatchedBridge moduleConfig", nil);
|
||||||
[performanceLogger markStartForTag:RCTPLNativeModulePrepareConfig];
|
[performanceLogger markStartForTag:RCTPLNativeModulePrepareConfig];
|
||||||
config = [weakSelf moduleConfig];
|
config = [weakSelf moduleConfig];
|
||||||
[performanceLogger markStopForTag:RCTPLNativeModulePrepareConfig];
|
[performanceLogger markStopForTag:RCTPLNativeModulePrepareConfig];
|
||||||
|
RCT_PROFILE_END_EVENT(0, @"", nil);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -166,6 +171,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)dele
|
||||||
[strongSelf executeSourceCode:sourceCode];
|
[strongSelf executeSourceCode:sourceCode];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
RCT_PROFILE_END_EVENT(0, @"", nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad
|
- (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad
|
||||||
|
@ -233,6 +240,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)dele
|
||||||
moduleData = _moduleDataByName[[@"RCT" stringByAppendingString:moduleName]];
|
moduleData = _moduleDataByName[[@"RCT" stringByAppendingString:moduleName]];
|
||||||
}
|
}
|
||||||
if (moduleData) {
|
if (moduleData) {
|
||||||
|
#if RCT_DEV
|
||||||
|
if ([self.delegate respondsToSelector:@selector(whitelistedModulesForBridge:)]) {
|
||||||
|
NSArray *whitelisted = [self.delegate whitelistedModulesForBridge:self];
|
||||||
|
RCTAssert(!whitelisted || [whitelisted containsObject:[moduleData moduleClass]],
|
||||||
|
@"Required config for %@, which was not whitelisted", moduleName);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return moduleData.config;
|
return moduleData.config;
|
||||||
}
|
}
|
||||||
return (id)kCFNull;
|
return (id)kCFNull;
|
||||||
|
@ -240,6 +254,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)dele
|
||||||
|
|
||||||
- (void)initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
|
- (void)initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
|
||||||
{
|
{
|
||||||
|
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBatchedBridge initModules]", nil);
|
||||||
[_performanceLogger markStartForTag:RCTPLNativeModuleInit];
|
[_performanceLogger markStartForTag:RCTPLNativeModuleInit];
|
||||||
|
|
||||||
NSArray<id<RCTBridgeModule>> *extraModules = nil;
|
NSArray<id<RCTBridgeModule>> *extraModules = nil;
|
||||||
|
@ -252,7 +267,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)dele
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RCT_DEBUG && !RCTRunningInTestEnvironment()) {
|
if (RCT_DEBUG && !RCTRunningInTestEnvironment()) {
|
||||||
|
|
||||||
// Check for unexported modules
|
// Check for unexported modules
|
||||||
static Class *classes;
|
static Class *classes;
|
||||||
static unsigned int classCount;
|
static unsigned int classCount;
|
||||||
|
@ -290,6 +304,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)dele
|
||||||
NSMutableDictionary<NSString *, RCTModuleData *> *moduleDataByName = [NSMutableDictionary new];
|
NSMutableDictionary<NSString *, RCTModuleData *> *moduleDataByName = [NSMutableDictionary new];
|
||||||
|
|
||||||
// Set up moduleData for pre-initialized module instances
|
// Set up moduleData for pre-initialized module instances
|
||||||
|
RCT_PROFILE_BEGIN_EVENT(0, @"extraModules", nil);
|
||||||
for (id<RCTBridgeModule> module in extraModules) {
|
for (id<RCTBridgeModule> module in extraModules) {
|
||||||
Class moduleClass = [module class];
|
Class moduleClass = [module class];
|
||||||
NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
|
NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
|
||||||
|
@ -317,11 +332,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)dele
|
||||||
_javaScriptExecutor = (id<RCTJavaScriptExecutor>)module;
|
_javaScriptExecutor = (id<RCTJavaScriptExecutor>)module;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RCT_PROFILE_END_EVENT(0, @"", nil);
|
||||||
|
|
||||||
// The executor is a bridge module, but we want it to be instantiated before
|
// The executor is a bridge module, but we want it to be instantiated before
|
||||||
// any other module has access to the bridge, in case they need the JS thread.
|
// any other module has access to the bridge, in case they need the JS thread.
|
||||||
// TODO: once we have more fine-grained control of init (t11106126) we can
|
// TODO: once we have more fine-grained control of init (t11106126) we can
|
||||||
// probably just replace this with [self moduleForClass:self.executorClass]
|
// probably just replace this with [self moduleForClass:self.executorClass]
|
||||||
|
RCT_PROFILE_BEGIN_EVENT(0, @"JavaScriptExecutor", nil);
|
||||||
if (!_javaScriptExecutor) {
|
if (!_javaScriptExecutor) {
|
||||||
id<RCTJavaScriptExecutor> executorModule = [self.executorClass new];
|
id<RCTJavaScriptExecutor> executorModule = [self.executorClass new];
|
||||||
RCTModuleData *moduleData = [[RCTModuleData alloc] initWithModuleInstance:executorModule
|
RCTModuleData *moduleData = [[RCTModuleData alloc] initWithModuleInstance:executorModule
|
||||||
|
@ -333,8 +350,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)dele
|
||||||
// NOTE: _javaScriptExecutor is a weak reference
|
// NOTE: _javaScriptExecutor is a weak reference
|
||||||
_javaScriptExecutor = executorModule;
|
_javaScriptExecutor = executorModule;
|
||||||
}
|
}
|
||||||
|
RCT_PROFILE_END_EVENT(0, @"", nil);
|
||||||
|
|
||||||
// Set up moduleData for automatically-exported modules
|
// Set up moduleData for automatically-exported modules
|
||||||
|
RCT_PROFILE_BEGIN_EVENT(0, @"ModuleData", nil);
|
||||||
for (Class moduleClass in RCTGetModuleClasses()) {
|
for (Class moduleClass in RCTGetModuleClasses()) {
|
||||||
NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
|
NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
|
||||||
|
|
||||||
|
@ -367,8 +386,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)dele
|
||||||
_moduleDataByID = [moduleDataByID copy];
|
_moduleDataByID = [moduleDataByID copy];
|
||||||
_moduleDataByName = [moduleDataByName copy];
|
_moduleDataByName = [moduleDataByName copy];
|
||||||
_moduleClassesByID = [moduleClassesByID copy];
|
_moduleClassesByID = [moduleClassesByID copy];
|
||||||
|
RCT_PROFILE_END_EVENT(0, @"", nil);
|
||||||
|
|
||||||
// Synchronously set up the pre-initialized modules
|
// Synchronously set up the pre-initialized modules
|
||||||
|
RCT_PROFILE_BEGIN_EVENT(0, @"extraModules", nil);
|
||||||
for (RCTModuleData *moduleData in _moduleDataByID) {
|
for (RCTModuleData *moduleData in _moduleDataByID) {
|
||||||
if (moduleData.hasInstance &&
|
if (moduleData.hasInstance &&
|
||||||
(!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) {
|
(!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) {
|
||||||
|
@ -381,39 +402,79 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)dele
|
||||||
(void)[moduleData instance];
|
(void)[moduleData instance];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RCT_PROFILE_END_EVENT(0, @"", nil);
|
||||||
|
|
||||||
// From this point on, RCTDidInitializeModuleNotification notifications will
|
// From this point on, RCTDidInitializeModuleNotification notifications will
|
||||||
// be sent the first time a module is accessed.
|
// be sent the first time a module is accessed.
|
||||||
_moduleSetupComplete = YES;
|
_moduleSetupComplete = YES;
|
||||||
|
|
||||||
|
[self prepareModulesWithDispatchGroup:dispatchGroup];
|
||||||
|
|
||||||
|
[_performanceLogger markStopForTag:RCTPLNativeModuleInit];
|
||||||
|
RCT_PROFILE_END_EVENT(0, @"", nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
|
||||||
|
{
|
||||||
|
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBatchedBridge prepareModulesWithDispatch]", nil);
|
||||||
|
|
||||||
|
NSArray<Class> *whitelistedModules = nil;
|
||||||
|
if ([self.delegate respondsToSelector:@selector(whitelistedModulesForBridge:)]) {
|
||||||
|
whitelistedModules = [self.delegate whitelistedModulesForBridge:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL initializeImmediately = NO;
|
||||||
|
if (dispatchGroup == NULL) {
|
||||||
|
// If no dispatchGroup is passed in, we must prepare everything immediately.
|
||||||
|
// We better be on the right thread too.
|
||||||
|
RCTAssertMainQueue();
|
||||||
|
initializeImmediately = YES;
|
||||||
|
} else if ([self.delegate respondsToSelector:@selector(shouldBridgeInitializeNativeModulesOnCurrentThread:)]) {
|
||||||
|
initializeImmediately = [self.delegate shouldBridgeInitializeNativeModulesOnCurrentThread:self];
|
||||||
|
}
|
||||||
|
|
||||||
// Set up modules that require main thread init or constants export
|
// Set up modules that require main thread init or constants export
|
||||||
[_performanceLogger setValue:0 forTag:RCTPLNativeModuleMainThread];
|
[_performanceLogger setValue:0 forTag:RCTPLNativeModuleMainThread];
|
||||||
NSUInteger modulesOnMainQueueCount = 0;
|
|
||||||
for (RCTModuleData *moduleData in _moduleDataByID) {
|
for (RCTModuleData *moduleData in _moduleDataByID) {
|
||||||
__weak RCTBatchedBridge *weakSelf = self;
|
if (whitelistedModules && ![whitelistedModules containsObject:[moduleData moduleClass]]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (moduleData.requiresMainQueueSetup || moduleData.hasConstantsToExport) {
|
if (moduleData.requiresMainQueueSetup || moduleData.hasConstantsToExport) {
|
||||||
// Modules that need to be set up on the main thread cannot be initialized
|
// Modules that need to be set up on the main thread cannot be initialized
|
||||||
// lazily when required without doing a dispatch_sync to the main thread,
|
// lazily when required without doing a dispatch_sync to the main thread,
|
||||||
// which can result in deadlock. To avoid this, we initialize all of these
|
// which can result in deadlock. To avoid this, we initialize all of these
|
||||||
// modules on the main thread in parallel with loading the JS code, so
|
// modules on the main thread in parallel with loading the JS code, so
|
||||||
// they will already be available before they are ever required.
|
// they will already be available before they are ever required.
|
||||||
dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), ^{
|
dispatch_block_t block = ^{
|
||||||
RCTBatchedBridge *strongSelf = weakSelf;
|
if (self.valid) {
|
||||||
if (!strongSelf.valid) {
|
[self->_performanceLogger appendStartForTag:RCTPLNativeModuleMainThread];
|
||||||
return;
|
(void)[moduleData instance];
|
||||||
|
[moduleData gatherConstants];
|
||||||
|
[self->_performanceLogger appendStopForTag:RCTPLNativeModuleMainThread];
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
[strongSelf->_performanceLogger appendStartForTag:RCTPLNativeModuleMainThread];
|
if (initializeImmediately && RCTIsMainQueue()) {
|
||||||
(void)[moduleData instance];
|
block();
|
||||||
[moduleData gatherConstants];
|
} else {
|
||||||
[strongSelf->_performanceLogger appendStopForTag:RCTPLNativeModuleMainThread];
|
// We've already checked that dispatchGroup is non-null, but this satisifies the
|
||||||
});
|
// Xcode analyzer
|
||||||
modulesOnMainQueueCount++;
|
if (dispatchGroup) {
|
||||||
|
dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_modulesInitializedOnMainQueue++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[_performanceLogger markStopForTag:RCTPLNativeModuleInit];
|
[_performanceLogger setValue:_modulesInitializedOnMainQueue forTag:RCTPLNativeModuleMainThreadUsesCount];
|
||||||
[_performanceLogger setValue:modulesOnMainQueueCount forTag:RCTPLNativeModuleMainThreadUsesCount];
|
RCT_PROFILE_END_EVENT(0, @"", nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)whitelistedModulesDidChange
|
||||||
|
{
|
||||||
|
[self prepareModulesWithDispatchGroup:NULL];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setUpExecutor
|
- (void)setUpExecutor
|
||||||
|
@ -594,23 +655,22 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||||
queue:(dispatch_queue_t)queue
|
queue:(dispatch_queue_t)queue
|
||||||
{
|
{
|
||||||
if (queue == RCTJSThread) {
|
if (queue == RCTJSThread) {
|
||||||
__weak __typeof(self) weakSelf = self;
|
|
||||||
RCTProfileBeginFlowEvent();
|
RCTProfileBeginFlowEvent();
|
||||||
RCTAssert(_javaScriptExecutor != nil, @"Need JS executor to schedule JS work");
|
RCTAssert(_javaScriptExecutor != nil, @"Need JS executor to schedule JS work");
|
||||||
|
|
||||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||||
RCTProfileEndFlowEvent();
|
RCTProfileEndFlowEvent();
|
||||||
|
|
||||||
RCTBatchedBridge *strongSelf = weakSelf;
|
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBatchedBridge dispatchBlock", @{ @"loading": @(self.loading) });
|
||||||
if (!strongSelf) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strongSelf.loading) {
|
if (self.loading) {
|
||||||
[strongSelf->_pendingCalls addObject:block];
|
RCTAssert(self->_pendingCalls != nil, @"Can't add pending call, bridge is no longer loading");
|
||||||
|
[self->_pendingCalls addObject:block];
|
||||||
} else {
|
} else {
|
||||||
block();
|
block();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_PROFILE_END_EVENT(0, @"", nil);
|
||||||
}];
|
}];
|
||||||
} else if (queue) {
|
} else if (queue) {
|
||||||
dispatch_async(queue, block);
|
dispatch_async(queue, block);
|
||||||
|
@ -700,11 +760,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||||
/**
|
/**
|
||||||
* AnyThread
|
* AnyThread
|
||||||
*/
|
*/
|
||||||
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTBatchedBridge enqueueJSCall:]", nil);
|
|
||||||
if (!_valid) {
|
if (!_valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTBatchedBridge enqueueJSCall:]", nil);
|
||||||
__weak __typeof(self) weakSelf = self;
|
__weak __typeof(self) weakSelf = self;
|
||||||
[self dispatchBlock:^{
|
[self dispatchBlock:^{
|
||||||
[weakSelf _actuallyInvokeAndProcessModule:module method:method arguments:args ?: @[]];
|
[weakSelf _actuallyInvokeAndProcessModule:module method:method arguments:args ?: @[]];
|
||||||
|
@ -712,7 +772,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||||
completion();
|
completion();
|
||||||
}
|
}
|
||||||
} queue:RCTJSThread];
|
} queue:RCTJSThread];
|
||||||
|
|
||||||
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"", nil);
|
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"", nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -786,7 +845,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||||
{
|
{
|
||||||
RCTAssertJSThread();
|
RCTAssertJSThread();
|
||||||
|
|
||||||
__weak typeof(self) weakSelf = self;
|
__weak __typeof(self) weakSelf = self;
|
||||||
[_javaScriptExecutor callFunctionOnModule:module
|
[_javaScriptExecutor callFunctionOnModule:module
|
||||||
method:method
|
method:method
|
||||||
arguments:args
|
arguments:args
|
||||||
|
@ -800,7 +859,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||||
{
|
{
|
||||||
RCTAssertJSThread();
|
RCTAssertJSThread();
|
||||||
|
|
||||||
__weak typeof(self) weakSelf = self;
|
__weak __typeof(self) weakSelf = self;
|
||||||
[_javaScriptExecutor invokeCallbackID:cbID
|
[_javaScriptExecutor invokeCallbackID:cbID
|
||||||
arguments:args
|
arguments:args
|
||||||
callback:^(id json, NSError *error) {
|
callback:^(id json, NSError *error) {
|
||||||
|
@ -926,7 +985,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||||
- (void)partialBatchDidFlush
|
- (void)partialBatchDidFlush
|
||||||
{
|
{
|
||||||
for (RCTModuleData *moduleData in _moduleDataByID) {
|
for (RCTModuleData *moduleData in _moduleDataByID) {
|
||||||
if (moduleData.implementsPartialBatchDidFlush) {
|
if (moduleData.hasInstance && moduleData.implementsPartialBatchDidFlush) {
|
||||||
[self dispatchBlock:^{
|
[self dispatchBlock:^{
|
||||||
[moduleData.instance partialBatchDidFlush];
|
[moduleData.instance partialBatchDidFlush];
|
||||||
} queue:moduleData.methodQueue];
|
} queue:moduleData.methodQueue];
|
||||||
|
@ -938,7 +997,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||||
{
|
{
|
||||||
// TODO: batchDidComplete is only used by RCTUIManager - can we eliminate this special case?
|
// TODO: batchDidComplete is only used by RCTUIManager - can we eliminate this special case?
|
||||||
for (RCTModuleData *moduleData in _moduleDataByID) {
|
for (RCTModuleData *moduleData in _moduleDataByID) {
|
||||||
if (moduleData.implementsBatchDidComplete) {
|
if (moduleData.hasInstance && moduleData.implementsBatchDidComplete) {
|
||||||
[self dispatchBlock:^{
|
[self dispatchBlock:^{
|
||||||
[moduleData.instance batchDidComplete];
|
[moduleData.instance batchDidComplete];
|
||||||
} queue:moduleData.methodQueue];
|
} queue:moduleData.methodQueue];
|
||||||
|
|
|
@ -126,6 +126,17 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
|
||||||
*/
|
*/
|
||||||
- (BOOL)moduleIsInitialized:(Class)moduleClass;
|
- (BOOL)moduleIsInitialized:(Class)moduleClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call when your delegate's `whitelistedModulesForBridge:` value has changed.
|
||||||
|
* In response to this, the bridge will instantiate any (whitelisted) native modules
|
||||||
|
* that require main thread initialization, so we don't depend on dispatch_sync
|
||||||
|
* later on.
|
||||||
|
*
|
||||||
|
* This method must be called on the main thread, as any pending native modules
|
||||||
|
* will be initialized immediately.
|
||||||
|
*/
|
||||||
|
- (void)whitelistedModulesDidChange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All registered bridge module classes.
|
* All registered bridge module classes.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
#import "RCTModuleData.h"
|
#import "RCTModuleData.h"
|
||||||
#import "RCTPerformanceLogger.h"
|
#import "RCTPerformanceLogger.h"
|
||||||
|
#import "RCTProfile.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
NSString *const RCTReloadNotification = @"RCTReloadNotification";
|
NSString *const RCTReloadNotification = @"RCTReloadNotification";
|
||||||
|
@ -176,7 +177,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||||
object:nil
|
object:nil
|
||||||
userInfo:nil];
|
userInfo:nil];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,6 +219,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||||
return [self.batchedBridge moduleIsInitialized:moduleClass];
|
return [self.batchedBridge moduleIsInitialized:moduleClass];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)whitelistedModulesDidChange
|
||||||
|
{
|
||||||
|
[self.batchedBridge whitelistedModulesDidChange];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)reload
|
- (void)reload
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -232,6 +237,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||||
|
|
||||||
- (void)setUp
|
- (void)setUp
|
||||||
{
|
{
|
||||||
|
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBridge setUp]", nil);
|
||||||
|
|
||||||
// Only update bundleURL from delegate if delegate bundleURL has changed
|
// Only update bundleURL from delegate if delegate bundleURL has changed
|
||||||
NSURL *previousDelegateURL = _delegateBundleURL;
|
NSURL *previousDelegateURL = _delegateBundleURL;
|
||||||
_delegateBundleURL = [self.delegate sourceURLForBridge:self];
|
_delegateBundleURL = [self.delegate sourceURLForBridge:self];
|
||||||
|
@ -243,6 +250,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||||
_bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
|
_bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
|
||||||
|
|
||||||
[self createBatchedBridge];
|
[self createBatchedBridge];
|
||||||
|
|
||||||
|
RCT_PROFILE_END_EVENT(0, @"", nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)createBatchedBridge
|
- (void)createBatchedBridge
|
||||||
|
|
|
@ -7,10 +7,11 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@class RCTBridge;
|
|
||||||
|
|
||||||
#import "RCTJavaScriptLoader.h"
|
#import "RCTJavaScriptLoader.h"
|
||||||
|
|
||||||
|
@class RCTBridge;
|
||||||
|
@protocol RCTBridgeModule;
|
||||||
|
|
||||||
@protocol RCTBridgeDelegate <NSObject>
|
@protocol RCTBridgeDelegate <NSObject>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,7 +47,28 @@
|
||||||
* not recommended in most cases - if the module methods and behavior do not
|
* not recommended in most cases - if the module methods and behavior do not
|
||||||
* match exactly, it may lead to bugs or crashes.
|
* match exactly, it may lead to bugs or crashes.
|
||||||
*/
|
*/
|
||||||
- (NSArray *)extraModulesForBridge:(RCTBridge *)bridge;
|
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customize how bridge native modules are initialized. By default all modules are
|
||||||
|
* created lazily except those that have constants to export or require main thread
|
||||||
|
* initialization. If you want to limit the subset of native modules that this
|
||||||
|
* should be considered for, implement this method. Return nil to whitelist all
|
||||||
|
* modules found. Modules passed in extraModulesForBridge: are automatically
|
||||||
|
* whitelisted.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
- (NSArray<Class> *)whitelistedModulesForBridge:(RCTBridge *)bridge;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When initializing main-thread-only native modules, the bridge will be default
|
||||||
|
* dispatch module creation blocks asynchrously. If we're blockingly waiting on
|
||||||
|
* the main thread to finish bridge creation on the main thread, this will deadlock.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
- (BOOL)shouldBridgeInitializeNativeModulesOnCurrentThread:(RCTBridge *)bridge;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The bridge will automatically attempt to load the JS source code from the
|
* The bridge will automatically attempt to load the JS source code from the
|
||||||
|
|
|
@ -83,14 +83,14 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
|
||||||
|
|
||||||
- (void)setUpInstanceAndBridge
|
- (void)setUpInstanceAndBridge
|
||||||
{
|
{
|
||||||
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData setUpInstanceAndBridge] [_instanceLock lock]", nil);
|
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData setUpInstanceAndBridge] [_instanceLock lock]", @{ @"moduleClass": _moduleClass });
|
||||||
[_instanceLock lock];
|
[_instanceLock lock];
|
||||||
if (!_setupComplete && _bridge.valid) {
|
if (!_setupComplete && _bridge.valid) {
|
||||||
if (!_instance) {
|
if (!_instance) {
|
||||||
if (RCT_DEBUG && _requiresMainQueueSetup) {
|
if (RCT_DEBUG && _requiresMainQueueSetup) {
|
||||||
RCTAssertMainQueue();
|
RCTAssertMainQueue();
|
||||||
}
|
}
|
||||||
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData setUpInstanceAndBridge] [_moduleClass new]", nil);
|
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData setUpInstanceAndBridge] [_moduleClass new]", @{ @"moduleClass": _moduleClass });
|
||||||
_instance = [_moduleClass new];
|
_instance = [_moduleClass new];
|
||||||
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"", nil);
|
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"", nil);
|
||||||
if (!_instance) {
|
if (!_instance) {
|
||||||
|
@ -216,6 +216,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
|
||||||
// get accessed by client code during bridge setup, and a very low risk of
|
// get accessed by client code during bridge setup, and a very low risk of
|
||||||
// deadlock is better than a fairly high risk of an assertion being thrown.
|
// deadlock is better than a fairly high risk of an assertion being thrown.
|
||||||
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData instance] main thread setup", nil);
|
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData instance] main thread setup", nil);
|
||||||
|
|
||||||
|
if (!RCTIsMainQueue()) {
|
||||||
|
RCTLogError(@"RCTBridge required dispatch_sync to load %@. This may lead to deadlocks", _moduleClass);
|
||||||
|
}
|
||||||
RCTExecuteOnMainThread(^{
|
RCTExecuteOnMainThread(^{
|
||||||
[self setUpInstanceAndBridge];
|
[self setUpInstanceAndBridge];
|
||||||
}, YES);
|
}, YES);
|
||||||
|
@ -278,6 +282,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
|
||||||
if (_hasConstantsToExport && !_constantsToExport) {
|
if (_hasConstantsToExport && !_constantsToExport) {
|
||||||
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, [NSString stringWithFormat:@"[RCTModuleData gatherConstants] %@", _moduleClass], nil);
|
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, [NSString stringWithFormat:@"[RCTModuleData gatherConstants] %@", _moduleClass], nil);
|
||||||
(void)[self instance];
|
(void)[self instance];
|
||||||
|
if (!RCTIsMainQueue()) {
|
||||||
|
RCTLogError(@"Required dispatch_sync to load constants for %@. This may lead to deadlocks", _moduleClass);
|
||||||
|
}
|
||||||
RCTExecuteOnMainThread(^{
|
RCTExecuteOnMainThread(^{
|
||||||
self->_constantsToExport = [self->_instance constantsToExport] ?: @{};
|
self->_constantsToExport = [self->_instance constantsToExport] ?: @{};
|
||||||
}, YES);
|
}, YES);
|
||||||
|
|
Loading…
Reference in New Issue