From e727fc817bd8a61d47387408637d53a30fc66f00 Mon Sep 17 00:00:00 2001 From: Alexey Lang Date: Tue, 29 Sep 2015 05:26:01 -0700 Subject: [PATCH] Pause JS DisplayLink if nothing to process. Reviewed By: @jspahrsummers Differential Revision: D2489107 --- React/Base/RCTBatchedBridge.m | 30 +++++++++++++++++++++++++++++- React/Base/RCTEventDispatcher.m | 16 ++++++++++++++-- React/Base/RCTFrameUpdate.h | 10 +++++++--- React/Modules/RCTTiming.m | 15 +++++++++++++-- React/Views/RCTNavigator.m | 15 +++++++++++++-- 5 files changed, 76 insertions(+), 10 deletions(-) diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index e4b8e7828..3559b58c3 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -309,6 +309,15 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); config[moduleData.name] = moduleData.config; if ([moduleData.instance conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) { [_frameUpdateObservers addObject:moduleData]; + + id observer = (id)moduleData.instance; + __weak typeof(self) weakSelf = self; + __weak typeof(_javaScriptExecutor) weakJavaScriptExecutor = _javaScriptExecutor; + observer.pauseCallback = ^{ + [weakJavaScriptExecutor executeBlockOnJavaScriptQueue:^{ + [weakSelf updateJSDisplayLinkState]; + }]; + }; } } @@ -317,6 +326,23 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); }, NULL); } +- (void)updateJSDisplayLinkState +{ + RCTAssertJSThread(); + + BOOL pauseDisplayLink = ![_scheduledCallbacks count] && ![_scheduledCalls count]; + if (pauseDisplayLink) { + for (RCTModuleData *moduleData in _frameUpdateObservers) { + id observer = (id)moduleData.instance; + if (!observer.paused) { + pauseDisplayLink = NO; + break; + } + } + } + _jsDisplayLink.paused = pauseDisplayLink; +} + - (void)injectJSONConfiguration:(NSString *)configJSON onComplete:(void (^)(NSError *))onComplete { @@ -620,6 +646,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR } else { [strongSelf->_scheduledCalls addObject:call]; } + [strongSelf updateJSDisplayLinkState]; RCTProfileEndEvent(0, @"objc_call", call); }]; @@ -804,7 +831,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink]; for (RCTModuleData *moduleData in _frameUpdateObservers) { id observer = (id)moduleData.instance; - if (![observer respondsToSelector:@selector(isPaused)] || !observer.paused) { + if (!observer.paused) { RCT_IF_DEV(NSString *name = [NSString stringWithFormat:@"[%@ didUpdateFrame:%f]", observer, displayLink.timestamp];) RCTProfileBeginFlowEvent(); @@ -833,6 +860,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR [self _actuallyInvokeAndProcessModule:@"BatchedBridge" method:@"processBatch" arguments:@[[calls valueForKey:@"js_args"]]]; + [self updateJSDisplayLinkState]; } RCTProfileEndEvent(0, @"objc_call", nil); diff --git a/React/Base/RCTEventDispatcher.m b/React/Base/RCTEventDispatcher.m index 484cf2f0a..48f98e457 100644 --- a/React/Base/RCTEventDispatcher.m +++ b/React/Base/RCTEventDispatcher.m @@ -93,18 +93,30 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) @synthesize bridge = _bridge; @synthesize paused = _paused; +@synthesize pauseCallback = _pauseCallback; RCT_EXPORT_MODULE() - (instancetype)init { if ((self = [super init])) { + _paused = YES; _eventQueue = [NSMutableDictionary new]; _eventQueueLock = [NSLock new]; } return self; } +- (void)setPaused:(BOOL)paused +{ + if (_paused != paused) { + _paused = paused; + if (_pauseCallback) { + _pauseCallback(); + } + } +} + - (void)sendAppEventWithName:(NSString *)name body:(id)body { [_bridge enqueueJSCall:@"RCTNativeAppEventEmitter.emit" @@ -169,7 +181,7 @@ RCT_EXPORT_MODULE() } _eventQueue[eventID] = event; - _paused = NO; + self.paused = NO; [_eventQueueLock unlock]; } @@ -202,7 +214,7 @@ RCT_EXPORT_MODULE() [_eventQueueLock lock]; NSDictionary *eventQueue = _eventQueue; _eventQueue = [NSMutableDictionary new]; - _paused = YES; + self.paused = YES; [_eventQueueLock unlock]; for (id event in eventQueue.allValues) { diff --git a/React/Base/RCTFrameUpdate.h b/React/Base/RCTFrameUpdate.h index f14bd5b86..bbf4bb6c9 100644 --- a/React/Base/RCTFrameUpdate.h +++ b/React/Base/RCTFrameUpdate.h @@ -40,11 +40,15 @@ */ - (void)didUpdateFrame:(RCTFrameUpdate *)update; -@optional - /** * Synthesize and set to true to pause the calls to -[didUpdateFrame:] */ -@property (nonatomic, assign, getter=isPaused) BOOL paused; +@property (nonatomic, readonly, getter=isPaused) BOOL paused; + +/** + * Callback for pause/resume observer. + * Observer should call it when paused property is changed. + */ +@property (nonatomic, copy) dispatch_block_t pauseCallback; @end diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m index 2754925e2..9da28d9df 100644 --- a/React/Modules/RCTTiming.m +++ b/React/Modules/RCTTiming.m @@ -71,6 +71,7 @@ @synthesize bridge = _bridge; @synthesize paused = _paused; +@synthesize pauseCallback = _pauseCallback; RCT_EXPORT_MODULE() @@ -120,7 +121,7 @@ RCT_EXPORT_MODULE() - (void)stopTimers { - _paused = YES; + self.paused = YES; } - (void)startTimers @@ -129,7 +130,17 @@ RCT_EXPORT_MODULE() return; } - _paused = NO; + self.paused = NO; +} + +- (void)setPaused:(BOOL)paused +{ + if (_paused != paused) { + _paused = paused; + if (_pauseCallback) { + _pauseCallback(); + } + } } - (void)didUpdateFrame:(__unused RCTFrameUpdate *)update diff --git a/React/Views/RCTNavigator.m b/React/Views/RCTNavigator.m index a79fe0992..8b67222aa 100644 --- a/React/Views/RCTNavigator.m +++ b/React/Views/RCTNavigator.m @@ -269,6 +269,7 @@ NSInteger kNeverProgressed = -10000; } @synthesize paused = _paused; +@synthesize pauseCallback = _pauseCallback; - (instancetype)initWithBridge:(RCTBridge *)bridge { @@ -321,6 +322,16 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) } } +- (void)setPaused:(BOOL)paused +{ + if (_paused != paused) { + _paused = paused; + if (_pauseCallback) { + _pauseCallback(); + } + } +} + - (void)dealloc { _navigationController.delegate = nil; @@ -355,14 +366,14 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) _dummyView.frame = (CGRect){{destination, 0}, CGSizeZero}; _currentlyTransitioningFrom = indexOfFrom; _currentlyTransitioningTo = indexOfTo; - _paused = NO; + self.paused = NO; } completion:^(__unused id context) { [weakSelf freeLock]; _currentlyTransitioningFrom = 0; _currentlyTransitioningTo = 0; _dummyView.frame = CGRectZero; - _paused = YES; + self.paused = YES; // Reset the parallel position tracker }]; }