mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-03-26 07:04:05 +08:00
Refactored module access to allow for lazy loading
Summary: public The `bridge.modules` dictionary provides access to all native modules, but this API requires that every module is initialized in advance so that any module can be accessed. This diff introduces a better API that will allow modules to be initialized lazily as they are needed, and deprecates `bridge.modules` (modules that use it will still work, but should be rewritten to use `bridge.moduleClasses` or `-[bridge moduleForName/Class:` instead. The rules are now as follows: * Any module that overrides `init` or `setBridge:` will be initialized on the main thread when the bridge is created * Any module that implements `constantsToExport:` will be initialized later when the config is exported (the module itself will be initialized on a background queue, but `constantsToExport:` will still be called on the main thread. * All other modules will be initialized lazily when a method is first called on them. These rules may seem slightly arcane, but they have the advantage of not violating any assumptions that may have been made by existing code - any module written under the original assumption that it would be initialized synchronously on the main thread when the bridge is created should still function exactly the same, but modules that avoid overriding `init` or `setBridge:` will now be loaded lazily. I've rewritten most of the standard modules to take advantage of this new lazy loading, with the following results: Out of the 65 modules included in UIExplorer: * 16 are initialized on the main thread when the bridge is created * A further 8 are initialized when the config is exported to JS * The remaining 41 will be initialized lazily on-demand Reviewed By: jspahrsummers Differential Revision: D2677695 fb-gh-sync-id: 507ae7e9fd6b563e89292c7371767c978e928f33
This commit is contained in:
committed by
facebook-github-bot-7
parent
bba71f146d
commit
060664fd3d
@@ -59,6 +59,8 @@ RCT_EXPORT_MODULE()
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
// TODO: can this be moved out of the startup path?
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(didReceiveNewContentSizeCategory:)
|
||||
name:UIContentSizeCategoryDidChangeNotification
|
||||
@@ -176,7 +178,7 @@ RCT_EXPORT_METHOD(getCurrentVoiceOverState:(RCTResponseSenderBlock)callback
|
||||
|
||||
- (RCTAccessibilityManager *)accessibilityManager
|
||||
{
|
||||
return self.modules[RCTBridgeModuleNameForClass([RCTAccessibilityManager class])];
|
||||
return [self moduleForClass:[RCTAccessibilityManager class]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -28,17 +28,6 @@
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_alerts = [NSMutableArray new];
|
||||
_alertControllers = [NSMutableArray new];
|
||||
_alertCallbacks = [NSMutableArray new];
|
||||
_alertButtonKeys = [NSMutableArray new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)methodQueue
|
||||
{
|
||||
return dispatch_get_main_queue();
|
||||
@@ -118,6 +107,11 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
|
||||
index ++;
|
||||
}
|
||||
|
||||
if (!_alerts) {
|
||||
_alerts = [NSMutableArray new];
|
||||
_alertCallbacks = [NSMutableArray new];
|
||||
_alertButtonKeys = [NSMutableArray new];
|
||||
}
|
||||
[_alerts addObject:alertView];
|
||||
[_alertCallbacks addObject:callback ?: ^(__unused id unused) {}];
|
||||
[_alertButtonKeys addObject:buttonKeys];
|
||||
@@ -175,6 +169,11 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
|
||||
}]];
|
||||
}
|
||||
|
||||
if (!_alertControllers) {
|
||||
_alertControllers = [NSMutableArray new];
|
||||
}
|
||||
[_alertControllers addObject:alertController];
|
||||
|
||||
[presentingController presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,27 +44,26 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)init
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_bridge = bridge;
|
||||
|
||||
_lastKnownState = RCTCurrentAppBackgroundState();
|
||||
|
||||
for (NSString *name in @[UIApplicationDidBecomeActiveNotification,
|
||||
UIApplicationDidEnterBackgroundNotification,
|
||||
UIApplicationDidFinishLaunchingNotification]) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleAppStateDidChange)
|
||||
name:name
|
||||
object:nil];
|
||||
}
|
||||
// Is this thread-safe?
|
||||
_lastKnownState = RCTCurrentAppBackgroundState();
|
||||
|
||||
for (NSString *name in @[UIApplicationDidBecomeActiveNotification,
|
||||
UIApplicationDidEnterBackgroundNotification,
|
||||
UIApplicationDidFinishLaunchingNotification]) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleMemoryWarning)
|
||||
name:UIApplicationDidReceiveMemoryWarningNotification
|
||||
selector:@selector(handleAppStateDidChange)
|
||||
name:name
|
||||
object:nil];
|
||||
}
|
||||
return self;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleMemoryWarning)
|
||||
name:UIApplicationDidReceiveMemoryWarningNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)handleMemoryWarning
|
||||
|
||||
@@ -34,23 +34,6 @@ RCT_EXPORT_MODULE()
|
||||
isEnabled = enabled;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(hide)
|
||||
name:RCTJavaScriptDidLoadNotification
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(hide)
|
||||
name:RCTJavaScriptDidFailToLoadNotification
|
||||
object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
@@ -59,6 +42,16 @@ RCT_EXPORT_MODULE()
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_bridge = bridge;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(hide)
|
||||
name:RCTJavaScriptDidLoadNotification
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(hide)
|
||||
name:RCTJavaScriptDidFailToLoadNotification
|
||||
object:nil];
|
||||
[self showWithURL:bridge.bundleURL];
|
||||
}
|
||||
|
||||
|
||||
@@ -321,7 +321,7 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
// Check if live reloading is available
|
||||
_liveReloadURL = nil;
|
||||
RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||
RCTSourceCode *sourceCodeModule = [_bridge moduleForClass:[RCTSourceCode class]];
|
||||
if (!sourceCodeModule.scriptURL) {
|
||||
if (!sourceCodeModule) {
|
||||
RCTLogWarn(@"RCTSourceCode module not found");
|
||||
@@ -614,7 +614,7 @@ RCT_EXPORT_METHOD(reload)
|
||||
- (RCTDevMenu *)devMenu
|
||||
{
|
||||
#if RCT_DEV
|
||||
return self.modules[RCTBridgeModuleNameForClass([RCTDevMenu class])];
|
||||
return [self moduleForClass:[RCTDevMenu class]];
|
||||
#else
|
||||
return nil;
|
||||
#endif
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
@interface RCTExceptionsManager : NSObject <RCTBridgeModule>
|
||||
|
||||
- (instancetype)initWithDelegate:(id<RCTExceptionsManagerDelegate>)delegate NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithDelegate:(id<RCTExceptionsManagerDelegate>)delegate;
|
||||
|
||||
@property (nonatomic, assign) NSUInteger maxReloadAttempts;
|
||||
|
||||
|
||||
@@ -27,18 +27,12 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
- (instancetype)initWithDelegate:(id<RCTExceptionsManagerDelegate>)delegate
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
if ((self = [self init])) {
|
||||
_delegate = delegate;
|
||||
_maxReloadAttempts = 0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithDelegate:nil];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(reportSoftException:(NSString *)message
|
||||
stack:(NSDictionaryArray *)stack
|
||||
exceptionId:(nonnull NSNumber *)exceptionId)
|
||||
|
||||
@@ -107,8 +107,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||
{
|
||||
if ((self.hidden && shouldShow) || (!self.hidden && [_lastErrorMessage isEqualToString:message])) {
|
||||
_lastStackTrace = stack;
|
||||
// message is displayed using UILabel, which is unable to render text of unlimited length, so we truncate it
|
||||
_lastErrorMessage = [message substringToIndex:MIN(10000, message.length)];
|
||||
// message is displayed using UILabel, which is unable to render text of
|
||||
// unlimited length, so we truncate it
|
||||
_lastErrorMessage = [message substringToIndex:MIN((NSUInteger)10000, message.length)];
|
||||
|
||||
[_stackTraceTableView reloadData];
|
||||
|
||||
@@ -326,7 +327,7 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
- (RCTRedBox *)redBox
|
||||
{
|
||||
return self.modules[RCTBridgeModuleNameForClass([RCTRedBox class])];
|
||||
return [self moduleForClass:[RCTRedBox class]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -46,14 +46,13 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
- (instancetype)init
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
[nc addObserver:self selector:@selector(applicationDidChangeStatusBarFrame:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
|
||||
[nc addObserver:self selector:@selector(applicationWillChangeStatusBarFrame:) name:UIApplicationWillChangeStatusBarFrameNotification object:nil];
|
||||
}
|
||||
return self;
|
||||
_bridge = bridge;
|
||||
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
[nc addObserver:self selector:@selector(applicationDidChangeStatusBarFrame:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
|
||||
[nc addObserver:self selector:@selector(applicationWillChangeStatusBarFrame:) name:UIApplicationWillChangeStatusBarFrameNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
|
||||
@@ -179,14 +179,6 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTUIManager ()
|
||||
|
||||
// NOTE: these are properties so that they can be accessed by unit tests
|
||||
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, RCTShadowView *> *shadowViewRegistry; // RCT thread only
|
||||
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIView *> *viewRegistry; // Main thread only
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTUIManager
|
||||
{
|
||||
dispatch_queue_t _shadowQueue;
|
||||
@@ -199,6 +191,9 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
|
||||
RCTLayoutAnimation *_nextLayoutAnimation; // RCT thread only
|
||||
RCTLayoutAnimation *_layoutAnimation; // Main thread only
|
||||
|
||||
NSMutableDictionary<NSNumber *, RCTShadowView *> *_shadowViewRegistry; // RCT thread only
|
||||
NSMutableDictionary<NSNumber *, UIView *> *_viewRegistry; // Main thread only
|
||||
|
||||
// Keyed by viewName
|
||||
NSDictionary *_componentDataByName;
|
||||
|
||||
@@ -214,31 +209,6 @@ RCT_EXPORT_MODULE()
|
||||
*/
|
||||
extern NSString *RCTBridgeModuleNameForClass(Class cls);
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
const char *queueName = "com.facebook.React.ShadowQueue";
|
||||
|
||||
if ([NSOperation instancesRespondToSelector:@selector(qualityOfService)]) {
|
||||
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, 0);
|
||||
_shadowQueue = dispatch_queue_create(queueName, attr);
|
||||
} else {
|
||||
_shadowQueue = dispatch_queue_create(queueName, DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_set_target_queue(_shadowQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
|
||||
}
|
||||
|
||||
_shadowViewRegistry = [NSMutableDictionary new];
|
||||
_viewRegistry = [NSMutableDictionary new];
|
||||
|
||||
// Internal resources
|
||||
_pendingUIBlocks = [NSMutableArray new];
|
||||
_rootViewTags = [NSMutableSet new];
|
||||
|
||||
_bridgeTransactionListeners = [NSMutableSet new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)didReceiveNewContentSizeMultiplier
|
||||
{
|
||||
__weak RCTUIManager *weakSelf = self;
|
||||
@@ -276,18 +246,45 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls);
|
||||
});
|
||||
}
|
||||
|
||||
- (NSMutableDictionary<NSNumber *, RCTShadowView *> *)shadowViewRegistry
|
||||
{
|
||||
// NOTE: this method only exists so that it can be accessed by unit tests
|
||||
if (!_shadowViewRegistry) {
|
||||
_shadowViewRegistry = [NSMutableDictionary new];
|
||||
}
|
||||
return _shadowViewRegistry;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary<NSNumber *, UIView *> *)viewRegistry
|
||||
{
|
||||
// NOTE: this method only exists so that it can be accessed by unit tests
|
||||
if (!_viewRegistry) {
|
||||
_viewRegistry = [NSMutableDictionary new];
|
||||
}
|
||||
return _viewRegistry;
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
RCTAssert(_bridge == nil, @"Should not re-use same UIIManager instance");
|
||||
|
||||
_bridge = bridge;
|
||||
|
||||
_shadowViewRegistry = [NSMutableDictionary new];
|
||||
_viewRegistry = [NSMutableDictionary new];
|
||||
|
||||
// Internal resources
|
||||
_pendingUIBlocks = [NSMutableArray new];
|
||||
_rootViewTags = [NSMutableSet new];
|
||||
|
||||
_bridgeTransactionListeners = [NSMutableSet new];
|
||||
|
||||
// Get view managers from bridge
|
||||
NSMutableDictionary *componentDataByName = [NSMutableDictionary new];
|
||||
for (RCTViewManager *manager in _bridge.modules.allValues) {
|
||||
if ([manager isKindOfClass:[RCTViewManager class]]) {
|
||||
RCTComponentData *componentData = [[RCTComponentData alloc] initWithManager:manager];
|
||||
for (Class moduleClass in _bridge.moduleClasses) {
|
||||
if ([moduleClass isSubclassOfClass:[RCTViewManager class]]) {
|
||||
RCTComponentData *componentData = [[RCTComponentData alloc] initWithManagerClass:moduleClass
|
||||
bridge:_bridge];
|
||||
componentDataByName[componentData.name] = componentData;
|
||||
}
|
||||
}
|
||||
@@ -302,6 +299,17 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls);
|
||||
|
||||
- (dispatch_queue_t)methodQueue
|
||||
{
|
||||
if (!_shadowQueue) {
|
||||
const char *queueName = "com.facebook.React.ShadowQueue";
|
||||
|
||||
if ([NSOperation instancesRespondToSelector:@selector(qualityOfService)]) {
|
||||
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, 0);
|
||||
_shadowQueue = dispatch_queue_create(queueName, attr);
|
||||
} else {
|
||||
_shadowQueue = dispatch_queue_create(queueName, DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_set_target_queue(_shadowQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
|
||||
}
|
||||
}
|
||||
return _shadowQueue;
|
||||
}
|
||||
|
||||
@@ -1126,12 +1134,11 @@ RCT_EXPORT_METHOD(clearJSResponder)
|
||||
[_componentDataByName enumerateKeysAndObjectsUsingBlock:
|
||||
^(NSString *name, RCTComponentData *componentData, __unused BOOL *stop) {
|
||||
|
||||
RCTViewManager *manager = componentData.manager;
|
||||
NSMutableDictionary<NSString *, id> *constantsNamespace =
|
||||
[NSMutableDictionary dictionaryWithDictionary:allJSConstants[name]];
|
||||
|
||||
// Add manager class
|
||||
constantsNamespace[@"Manager"] = RCTBridgeModuleNameForClass([manager class]);
|
||||
constantsNamespace[@"Manager"] = RCTBridgeModuleNameForClass(componentData.managerClass);
|
||||
|
||||
// Add native props
|
||||
NSDictionary<NSString *, id> *viewConfig = [componentData viewConfig];
|
||||
@@ -1216,7 +1223,7 @@ static UIView *_jsResponder;
|
||||
|
||||
- (RCTUIManager *)uiManager
|
||||
{
|
||||
return self.modules[RCTBridgeModuleNameForClass([RCTUIManager class])];
|
||||
return [self moduleForClass:[RCTUIManager class]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user