@@ -96,6 +96,7 @@ @implementation RCTTiming
96
96
NSMutableDictionary <NSNumber *, _RCTTimer *> *_timers;
97
97
NSTimer *_sleepTimer;
98
98
BOOL _sendIdleEvents;
99
+ BOOL _inBackground;
99
100
}
100
101
101
102
@synthesize bridge = _bridge;
@@ -110,20 +111,21 @@ - (void)setBridge:(RCTBridge *)bridge
110
111
111
112
_paused = YES ;
112
113
_timers = [NSMutableDictionary new ];
114
+ _inBackground = NO ;
113
115
114
116
for (NSString *name in @[UIApplicationWillResignActiveNotification,
115
117
UIApplicationDidEnterBackgroundNotification,
116
118
UIApplicationWillTerminateNotification ]) {
117
119
[[NSNotificationCenter defaultCenter ] addObserver: self
118
- selector: @selector (stopTimers )
120
+ selector: @selector (appDidMoveToBackground )
119
121
name: name
120
122
object: nil ];
121
123
}
122
124
123
125
for (NSString *name in @[UIApplicationDidBecomeActiveNotification,
124
126
UIApplicationWillEnterForegroundNotification ]) {
125
127
[[NSNotificationCenter defaultCenter ] addObserver: self
126
- selector: @selector (startTimers )
128
+ selector: @selector (appDidMoveToForeground )
127
129
name: name
128
130
object: nil ];
129
131
}
@@ -148,8 +150,29 @@ - (void)invalidate
148
150
_bridge = nil ;
149
151
}
150
152
153
+ - (void )appDidMoveToBackground
154
+ {
155
+ // Deactivate the CADisplayLink while in the background.
156
+ [self stopTimers ];
157
+ _inBackground = YES ;
158
+
159
+ // Issue one final timer callback, which will schedule a
160
+ // background NSTimer, if needed.
161
+ [self didUpdateFrame: nil ];
162
+ }
163
+
164
+ - (void )appDidMoveToForeground
165
+ {
166
+ _inBackground = NO ;
167
+ [self startTimers ];
168
+ }
169
+
151
170
- (void )stopTimers
152
171
{
172
+ if (_inBackground) {
173
+ return ;
174
+ }
175
+
153
176
if (!_paused) {
154
177
_paused = YES ;
155
178
if (_pauseCallback) {
@@ -160,7 +183,7 @@ - (void)stopTimers
160
183
161
184
- (void )startTimers
162
185
{
163
- if (!_bridge || ![self hasPendingTimers ]) {
186
+ if (!_bridge || _inBackground || ![self hasPendingTimers ]) {
164
187
return ;
165
188
}
166
189
@@ -225,7 +248,11 @@ - (void)didUpdateFrame:(RCTFrameUpdate *)update
225
248
// Switch to a paused state only if we didn't call any timer this frame, so if
226
249
// in response to this timer another timer is scheduled, we don't pause and unpause
227
250
// the displaylink frivolously.
228
- if (!_sendIdleEvents && timersToCall.count == 0 ) {
251
+ if (_inBackground) {
252
+ if (_timers.count ) {
253
+ [self scheduleSleepTimer: nextScheduledTarget];
254
+ }
255
+ } else if (!_sendIdleEvents && timersToCall.count == 0 ) {
229
256
// No need to call the pauseCallback as RCTDisplayLink will ask us about our paused
230
257
// status immediately after completing this call
231
258
if (_timers.count == 0 ) {
@@ -295,7 +322,10 @@ - (void)timerDidFire
295
322
targetTime: targetTime
296
323
repeats: repeats];
297
324
_timers[callbackID] = timer;
298
- if (_paused) {
325
+
326
+ if (_inBackground) {
327
+ [self scheduleSleepTimer: timer.target];
328
+ } else if (_paused) {
299
329
if ([timer.target timeIntervalSinceNow ] > kMinimumSleepInterval ) {
300
330
[self scheduleSleepTimer: timer.target];
301
331
} else {
0 commit comments