Skip to content

Commit e727fc8

Browse files
alexeylangfacebook-github-bot-4
authored andcommitted
Pause JS DisplayLink if nothing to process.
Reviewed By: @jspahrsummers Differential Revision: D2489107
1 parent 4c74f01 commit e727fc8

5 files changed

Lines changed: 76 additions & 10 deletions

File tree

React/Base/RCTBatchedBridge.m

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,15 @@ - (NSString *)moduleConfig
309309
config[moduleData.name] = moduleData.config;
310310
if ([moduleData.instance conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
311311
[_frameUpdateObservers addObject:moduleData];
312+
313+
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleData.instance;
314+
__weak typeof(self) weakSelf = self;
315+
__weak typeof(_javaScriptExecutor) weakJavaScriptExecutor = _javaScriptExecutor;
316+
observer.pauseCallback = ^{
317+
[weakJavaScriptExecutor executeBlockOnJavaScriptQueue:^{
318+
[weakSelf updateJSDisplayLinkState];
319+
}];
320+
};
312321
}
313322
}
314323

@@ -317,6 +326,23 @@ - (NSString *)moduleConfig
317326
}, NULL);
318327
}
319328

329+
- (void)updateJSDisplayLinkState
330+
{
331+
RCTAssertJSThread();
332+
333+
BOOL pauseDisplayLink = ![_scheduledCallbacks count] && ![_scheduledCalls count];
334+
if (pauseDisplayLink) {
335+
for (RCTModuleData *moduleData in _frameUpdateObservers) {
336+
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleData.instance;
337+
if (!observer.paused) {
338+
pauseDisplayLink = NO;
339+
break;
340+
}
341+
}
342+
}
343+
_jsDisplayLink.paused = pauseDisplayLink;
344+
}
345+
320346
- (void)injectJSONConfiguration:(NSString *)configJSON
321347
onComplete:(void (^)(NSError *))onComplete
322348
{
@@ -620,6 +646,7 @@ - (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arg
620646
} else {
621647
[strongSelf->_scheduledCalls addObject:call];
622648
}
649+
[strongSelf updateJSDisplayLinkState];
623650

624651
RCTProfileEndEvent(0, @"objc_call", call);
625652
}];
@@ -804,7 +831,7 @@ - (void)_jsThreadUpdate:(CADisplayLink *)displayLink
804831
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
805832
for (RCTModuleData *moduleData in _frameUpdateObservers) {
806833
id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleData.instance;
807-
if (![observer respondsToSelector:@selector(isPaused)] || !observer.paused) {
834+
if (!observer.paused) {
808835
RCT_IF_DEV(NSString *name = [NSString stringWithFormat:@"[%@ didUpdateFrame:%f]", observer, displayLink.timestamp];)
809836
RCTProfileBeginFlowEvent();
810837

@@ -833,6 +860,7 @@ - (void)_jsThreadUpdate:(CADisplayLink *)displayLink
833860
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
834861
method:@"processBatch"
835862
arguments:@[[calls valueForKey:@"js_args"]]];
863+
[self updateJSDisplayLinkState];
836864
}
837865

838866
RCTProfileEndEvent(0, @"objc_call", nil);

React/Base/RCTEventDispatcher.m

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,30 @@ @implementation RCTEventDispatcher
9393

9494
@synthesize bridge = _bridge;
9595
@synthesize paused = _paused;
96+
@synthesize pauseCallback = _pauseCallback;
9697

9798
RCT_EXPORT_MODULE()
9899

99100
- (instancetype)init
100101
{
101102
if ((self = [super init])) {
103+
_paused = YES;
102104
_eventQueue = [NSMutableDictionary new];
103105
_eventQueueLock = [NSLock new];
104106
}
105107
return self;
106108
}
107109

110+
- (void)setPaused:(BOOL)paused
111+
{
112+
if (_paused != paused) {
113+
_paused = paused;
114+
if (_pauseCallback) {
115+
_pauseCallback();
116+
}
117+
}
118+
}
119+
108120
- (void)sendAppEventWithName:(NSString *)name body:(id)body
109121
{
110122
[_bridge enqueueJSCall:@"RCTNativeAppEventEmitter.emit"
@@ -169,7 +181,7 @@ - (void)sendEvent:(id<RCTEvent>)event
169181
}
170182

171183
_eventQueue[eventID] = event;
172-
_paused = NO;
184+
self.paused = NO;
173185

174186
[_eventQueueLock unlock];
175187
}
@@ -202,7 +214,7 @@ - (void)didUpdateFrame:(__unused RCTFrameUpdate *)update
202214
[_eventQueueLock lock];
203215
NSDictionary *eventQueue = _eventQueue;
204216
_eventQueue = [NSMutableDictionary new];
205-
_paused = YES;
217+
self.paused = YES;
206218
[_eventQueueLock unlock];
207219

208220
for (id<RCTEvent> event in eventQueue.allValues) {

React/Base/RCTFrameUpdate.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,15 @@
4040
*/
4141
- (void)didUpdateFrame:(RCTFrameUpdate *)update;
4242

43-
@optional
44-
4543
/**
4644
* Synthesize and set to true to pause the calls to -[didUpdateFrame:]
4745
*/
48-
@property (nonatomic, assign, getter=isPaused) BOOL paused;
46+
@property (nonatomic, readonly, getter=isPaused) BOOL paused;
47+
48+
/**
49+
* Callback for pause/resume observer.
50+
* Observer should call it when paused property is changed.
51+
*/
52+
@property (nonatomic, copy) dispatch_block_t pauseCallback;
4953

5054
@end

React/Modules/RCTTiming.m

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ @implementation RCTTiming
7171

7272
@synthesize bridge = _bridge;
7373
@synthesize paused = _paused;
74+
@synthesize pauseCallback = _pauseCallback;
7475

7576
RCT_EXPORT_MODULE()
7677

@@ -120,7 +121,7 @@ - (void)invalidate
120121

121122
- (void)stopTimers
122123
{
123-
_paused = YES;
124+
self.paused = YES;
124125
}
125126

126127
- (void)startTimers
@@ -129,7 +130,17 @@ - (void)startTimers
129130
return;
130131
}
131132

132-
_paused = NO;
133+
self.paused = NO;
134+
}
135+
136+
- (void)setPaused:(BOOL)paused
137+
{
138+
if (_paused != paused) {
139+
_paused = paused;
140+
if (_pauseCallback) {
141+
_pauseCallback();
142+
}
143+
}
133144
}
134145

135146
- (void)didUpdateFrame:(__unused RCTFrameUpdate *)update

React/Views/RCTNavigator.m

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ @implementation RCTNavigator
269269
}
270270

271271
@synthesize paused = _paused;
272+
@synthesize pauseCallback = _pauseCallback;
272273

273274
- (instancetype)initWithBridge:(RCTBridge *)bridge
274275
{
@@ -321,6 +322,16 @@ - (void)didUpdateFrame:(__unused RCTFrameUpdate *)update
321322
}
322323
}
323324

325+
- (void)setPaused:(BOOL)paused
326+
{
327+
if (_paused != paused) {
328+
_paused = paused;
329+
if (_pauseCallback) {
330+
_pauseCallback();
331+
}
332+
}
333+
}
334+
324335
- (void)dealloc
325336
{
326337
_navigationController.delegate = nil;
@@ -355,14 +366,14 @@ - (void)navigationController:(UINavigationController *)navigationController
355366
_dummyView.frame = (CGRect){{destination, 0}, CGSizeZero};
356367
_currentlyTransitioningFrom = indexOfFrom;
357368
_currentlyTransitioningTo = indexOfTo;
358-
_paused = NO;
369+
self.paused = NO;
359370
}
360371
completion:^(__unused id<UIViewControllerTransitionCoordinatorContext> context) {
361372
[weakSelf freeLock];
362373
_currentlyTransitioningFrom = 0;
363374
_currentlyTransitioningTo = 0;
364375
_dummyView.frame = CGRectZero;
365-
_paused = YES;
376+
self.paused = YES;
366377
// Reset the parallel position tracker
367378
}];
368379
}

0 commit comments

Comments
 (0)