Initialize bridge on a background queue
Summary: This diff adds support for initializing the bridge on an arbitrary thread. This is helpful if you want to defer bridge creation, or prevent it from delaying your app startup. Reviewed By: javache Differential Revision: D2965725 fb-gh-sync-id: 8065fa89e850031c72ee4427351300986985e9de shipit-source-id: 8065fa89e850031c72ee4427351300986985e9de
This commit is contained in:
parent
7dbba3ba5f
commit
006907bdaa
|
@ -67,7 +67,6 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
|
|||
|
||||
- (instancetype)initWithParentBridge:(RCTBridge *)bridge
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
RCTAssertParam(bridge);
|
||||
|
||||
if ((self = [super initWithBundleURL:bridge.bundleURL
|
||||
|
@ -222,6 +221,11 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
|
|||
return _moduleDataByName[moduleName].instance;
|
||||
}
|
||||
|
||||
- (BOOL)moduleIsInitialized:(Class)moduleClass
|
||||
{
|
||||
return _moduleDataByName[RCTBridgeModuleNameForClass(moduleClass)].hasInstance;
|
||||
}
|
||||
|
||||
- (NSArray *)configForModuleName:(NSString *)moduleName
|
||||
{
|
||||
RCTModuleData *moduleData = _moduleDataByName[moduleName];
|
||||
|
@ -236,7 +240,6 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
|
|||
|
||||
- (void)initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
RCTPerformanceLoggerStart(RCTPLNativeModuleInit);
|
||||
|
||||
NSArray<id<RCTBridgeModule>> *extraModules = nil;
|
||||
|
@ -356,26 +359,39 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
|
|||
// trigger the lazy initialization process.
|
||||
(void)[moduleData instance];
|
||||
}
|
||||
}
|
||||
|
||||
// From this point on, RCTDidInitializeModuleNotification notifications will
|
||||
// be sent the first time a module is accessed.
|
||||
_moduleSetupComplete = YES;
|
||||
|
||||
// Set up modules that require main thread init or constants export
|
||||
for (RCTModuleData *moduleData in _moduleDataByID) {
|
||||
__weak RCTBatchedBridge *weakSelf = self;
|
||||
if (moduleData.requiresMainThreadSetup) {
|
||||
// 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,
|
||||
// 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 that
|
||||
// they will already be available before they are ever required.
|
||||
__weak RCTBatchedBridge *weakSelf = self;
|
||||
dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.valid) {
|
||||
(void)[moduleData instance];
|
||||
[moduleData gatherConstants];
|
||||
}
|
||||
});
|
||||
} else if (moduleData.hasConstantsToExport) {
|
||||
// Constants must be exported on the main thread, but module setup can
|
||||
// be done on any queue
|
||||
(void)[moduleData instance];
|
||||
dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.valid) {
|
||||
[moduleData gatherConstants];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// From this point on, RCTDidInitializeModuleNotification notifications will
|
||||
// be sent the first time a module is accessed.
|
||||
_moduleSetupComplete = YES;
|
||||
|
||||
RCTPerformanceLoggerEnd(RCTPLNativeModuleInit);
|
||||
}
|
||||
|
||||
|
|
|
@ -122,6 +122,13 @@ RCT_EXTERN BOOL RCTBridgeModuleClassIsRegistered(Class);
|
|||
*/
|
||||
- (NSArray *)modulesConformingToProtocol:(Protocol *)protocol;
|
||||
|
||||
/**
|
||||
* Test if a module has been initialized. Use this prior to calling
|
||||
* `moduleForClass:` or `moduleForName:` if you do not want to cause the module
|
||||
* to be instantiated if it hasn't been already.
|
||||
*/
|
||||
- (BOOL)moduleIsInitialized:(Class)moduleClass;
|
||||
|
||||
/**
|
||||
* All registered bridge module classes.
|
||||
*/
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTKeyCommands.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTModuleData.h"
|
||||
#import "RCTPerformanceLogger.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
|
@ -128,15 +129,13 @@ static RCTBridge *RCTCurrentBridgeInstance = nil;
|
|||
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
|
||||
launchOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if ((self = [super init])) {
|
||||
RCTPerformanceLoggerStart(RCTPLTTI);
|
||||
|
||||
_delegate = delegate;
|
||||
_launchOptions = [launchOptions copy];
|
||||
[self setUp];
|
||||
[self bindKeys];
|
||||
RCTExecuteOnMainThread(^{ [self bindKeys]; }, NO);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -145,8 +144,6 @@ static RCTBridge *RCTCurrentBridgeInstance = nil;
|
|||
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
||||
launchOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if ((self = [super init])) {
|
||||
RCTPerformanceLoggerStart(RCTPLTTI);
|
||||
|
||||
|
@ -154,7 +151,7 @@ static RCTBridge *RCTCurrentBridgeInstance = nil;
|
|||
_moduleProvider = block;
|
||||
_launchOptions = [launchOptions copy];
|
||||
[self setUp];
|
||||
[self bindKeys];
|
||||
RCTExecuteOnMainThread(^{ [self bindKeys]; }, NO);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -224,6 +221,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
return [modules copy];
|
||||
}
|
||||
|
||||
- (BOOL)moduleIsInitialized:(Class)moduleClass
|
||||
{
|
||||
return [self.batchedBridge moduleIsInitialized:moduleClass];
|
||||
}
|
||||
|
||||
- (RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
return [self moduleForClass:[RCTEventDispatcher class]];
|
||||
|
@ -232,7 +234,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
- (void)reload
|
||||
{
|
||||
/**
|
||||
* AnyThread
|
||||
* Any thread
|
||||
*/
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self invalidate];
|
||||
|
@ -242,8 +244,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
|
||||
- (void)setUp
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
// Only update bundleURL from delegate if delegate bundleURL has changed
|
||||
NSURL *previousDelegateURL = _delegateBundleURL;
|
||||
_delegateBundleURL = [self.delegate sourceURLForBridge:self];
|
||||
|
|
|
@ -50,6 +50,11 @@
|
|||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL requiresMainThreadSetup;
|
||||
|
||||
/**
|
||||
* Returns YES if module has constants to export.
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL hasConstantsToExport;
|
||||
|
||||
/**
|
||||
* Returns the current module instance. Note that this will init the instance
|
||||
* if it has not already been created. To check if the module instance exists
|
||||
|
|
|
@ -43,13 +43,16 @@
|
|||
setBridgeSelector = NSSelectorFromString(@"setBridge:");
|
||||
});
|
||||
|
||||
// If a module overrides `init`, `setBridge:`, or `constantsToExport` then we
|
||||
// must assume that it expects for both of those methods to be called on the
|
||||
// main thread, because those methods often need to access UIKit.
|
||||
_requiresMainThreadSetup = _instance ||
|
||||
// If a module overrides `init`, `setBridge:` then we must assume that it
|
||||
// expects for both of those methods to be called on the main thread, because
|
||||
// they may need to access UIKit.
|
||||
_requiresMainThreadSetup =
|
||||
[_moduleClass instancesRespondToSelector:setBridgeSelector] ||
|
||||
RCTClassOverridesInstanceMethod(_moduleClass, @selector(constantsToExport)) ||
|
||||
[_moduleClass instanceMethodForSelector:@selector(init)] != objectInitMethod;
|
||||
(!_instance && [_moduleClass instanceMethodForSelector:@selector(init)] != objectInitMethod);
|
||||
|
||||
// If a module overrides `constantsToExport` then we must assume that it
|
||||
// must be called on the main thread, because it may need to access UIKit.
|
||||
_hasConstantsToExport = RCTClassOverridesInstanceMethod(_moduleClass, @selector(constantsToExport));
|
||||
}
|
||||
|
||||
- (instancetype)initWithModuleClass:(Class)moduleClass
|
||||
|
@ -145,12 +148,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
|
|||
|
||||
- (void)setUpMethodQueue
|
||||
{
|
||||
if (_instance && !_methodQueue) {
|
||||
if (_instance && !_methodQueue && _bridge.valid) {
|
||||
BOOL implementsMethodQueue = [_instance respondsToSelector:@selector(methodQueue)];
|
||||
if (implementsMethodQueue) {
|
||||
if (implementsMethodQueue && _bridge.valid) {
|
||||
_methodQueue = _instance.methodQueue;
|
||||
}
|
||||
if (!_methodQueue) {
|
||||
if (!_methodQueue && _bridge.valid) {
|
||||
|
||||
// Create new queue (store queueName, as it isn't retained by dispatch_queue)
|
||||
_queueName = [NSString stringWithFormat:@"com.facebook.React.%@Queue", self.name];
|
||||
|
@ -240,15 +243,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
|
|||
|
||||
- (void)gatherConstants
|
||||
{
|
||||
if (!_constantsToExport) {
|
||||
if (RCTClassOverridesInstanceMethod(_moduleClass, @selector(constantsToExport))) {
|
||||
RCTExecuteOnMainThread(^{
|
||||
[self setUpInstanceAndBridge];
|
||||
_constantsToExport = [_instance constantsToExport] ?: @{};
|
||||
}, YES);
|
||||
} else {
|
||||
_constantsToExport = @{};
|
||||
}
|
||||
if (_hasConstantsToExport && !_constantsToExport) {
|
||||
(void)[self instance];
|
||||
RCTExecuteOnMainThread(^{
|
||||
_constantsToExport = [_instance constantsToExport] ?: @{};
|
||||
}, YES);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,8 +53,8 @@ RCT_EXPORT_MODULE()
|
|||
- (dispatch_queue_t)methodQueue
|
||||
{
|
||||
RCTAssert(_bridge, @"Bridge not set");
|
||||
RCTAssert(_bridge.uiManager, @"UIManager not initialized");
|
||||
RCTAssert(_bridge.uiManager.methodQueue, @"UIManager.methodQueue not initialized");
|
||||
RCTAssert(_bridge.uiManager || !_bridge.valid, @"UIManager not initialized");
|
||||
RCTAssert(_bridge.uiManager.methodQueue || !_bridge.valid, @"UIManager.methodQueue not initialized");
|
||||
return _bridge.uiManager.methodQueue;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue