[ReactNative] Use a single DisplayLink held by the bridge

This commit is contained in:
Tadeu Zagallo
2015-04-15 07:07:48 -08:00
parent 4f8b2825a0
commit 75e4e124fa
9 changed files with 176 additions and 59 deletions
+11
View File
@@ -10,6 +10,7 @@
#import <UIKit/UIKit.h>
#import "RCTBridgeModule.h"
#import "RCTFrameUpdate.h"
#import "RCTInvalidating.h"
#import "RCTJavaScriptExecutor.h"
@@ -122,4 +123,14 @@ static const char *__rct_import_##module##_##method##__ = #module"."#method;
*/
- (void)reload;
/**
* Add a new observer that will be called on every screen refresh
*/
- (void)addFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer;
/**
* Stop receiving screen refresh updates for the given observer
*/
- (void)removeFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer;
@end
+94
View File
@@ -677,6 +677,73 @@ static NSDictionary *RCTLocalModulesConfig()
return localModules;
}
@interface RCTDisplayLink : NSObject <RCTInvalidating>
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
@end
@interface RCTBridge (RCTDisplayLink)
- (void)_update:(CADisplayLink *)displayLink;
@end
@implementation RCTDisplayLink
{
__weak RCTBridge *_bridge;
CADisplayLink *_displayLink;
}
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
if ((self = [super init])) {
_bridge = bridge;
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_update:)];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
return self;
}
- (BOOL)isValid
{
return _displayLink != nil;
}
- (void)invalidate
{
if (self.isValid) {
[_displayLink invalidate];
_displayLink = nil;
}
}
- (void)_update:(CADisplayLink *)displayLink
{
[_bridge _update:displayLink];
}
@end
@interface RCTFrameUpdate (Private)
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink;
@end
@implementation RCTFrameUpdate
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink
{
if ((self = [super init])) {
_timestamp = displayLink.timestamp;
_deltaTime = displayLink.duration;
}
return self;
}
@end
@implementation RCTBridge
{
RCTSparseArray *_modulesByID;
@@ -685,6 +752,8 @@ static NSDictionary *RCTLocalModulesConfig()
Class _executorClass;
NSURL *_bundleURL;
RCTBridgeModuleProviderBlock _moduleProvider;
RCTDisplayLink *_displayLink;
NSMutableSet *_frameUpdateObservers;
BOOL _loading;
}
@@ -711,6 +780,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
_latestJSExecutor = _javaScriptExecutor;
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
_shadowQueue = dispatch_queue_create("com.facebook.React.ShadowQueue", DISPATCH_QUEUE_SERIAL);
_displayLink = [[RCTDisplayLink alloc] initWithBridge:self];
_frameUpdateObservers = [[NSMutableSet alloc] init];
// Register passed-in module instances
NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init];
@@ -891,6 +962,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
[_javaScriptExecutor invalidate];
_javaScriptExecutor = nil;
[_displayLink invalidate];
_frameUpdateObservers = nil;
// Invalidate modules
for (id target in _modulesByID.allObjects) {
if ([target respondsToSelector:@selector(invalidate)]) {
@@ -1075,6 +1149,26 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
return YES;
}
- (void)_update:(CADisplayLink *)displayLink
{
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
for (id<RCTFrameUpdateObserver> observer in _frameUpdateObservers) {
if (![observer respondsToSelector:@selector(isPaused)] || ![observer isPaused]) {
[observer didUpdateFrame:frameUpdate];
}
}
}
- (void)addFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer
{
[_frameUpdateObservers addObject:observer];
}
- (void)removeFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer
{
[_frameUpdateObservers removeObject:observer];
}
- (void)reload
{
if (!_loading) {
+44
View File
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/**
* Interface containing the information about the last screen refresh.
*/
@interface RCTFrameUpdate : NSObject
/**
* Timestamp for the actual screen refresh
*/
@property (nonatomic, readonly) NSTimeInterval timestamp;
/**
* Time since the last frame update ( >= 16.6ms )
*/
@property (nonatomic, readonly) NSTimeInterval deltaTime;
@end
/**
* Protocol that must be implemented for subscribing to display refreshes (DisplayLink updates)
*/
@protocol RCTFrameUpdateObserver <NSObject>
/**
* Method called on every screen refresh (if paused != YES)
*/
- (void)didUpdateFrame:(RCTFrameUpdate *)update;
@optional
/**
* Synthesize and set to true to pause the calls to -[didUpdateFrame:]
*/
@property (nonatomic, assign, getter=isPaused) BOOL paused;
@end