mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add option for deterministic Animated batching on iOS (#49942)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/49942 iOS flushes NativeAnimated operations to the UI thread in one of two ways: 1. Some operations flush immediately when the operation is queued, namely startAnimation, stopAnimation, and setValue. 2. Other operations rely on flushing from one of the previous operation types, or a UIManager mount callback. This behavior leads to non-determinism in flushing of some operations like `setOffset` or `startListeningToAnimatedNodeValue`, where if these operations are created, out-of-band from "flushed" operations, they will not take affect until the next React commit. This change wires up the `animatedShouldSignalBatch` feature flag to queue operations on the JS thread, and flush them after `NativeAnimated.finishOperationBatch` is called. ## Changelog [Internal] Reviewed By: javache Differential Revision: D70904592 fbshipit-source-id: 6bc7a8614cbe5e275d4d7bf5310da9f0090c0e10
This commit is contained in:
committed by
Facebook GitHub Bot
parent
6838d46b70
commit
8673294806
@@ -10,6 +10,7 @@
|
||||
#import <React/RCTInitializing.h>
|
||||
#import <React/RCTNativeAnimatedNodesManager.h>
|
||||
#import <React/RCTNativeAnimatedTurboModule.h>
|
||||
#import <react/featureflags/ReactNativeFeatureFlags.h>
|
||||
|
||||
#import "RCTAnimationPlugins.h"
|
||||
|
||||
@@ -73,52 +74,42 @@ RCT_EXPORT_MODULE();
|
||||
|
||||
#pragma mark-- API
|
||||
|
||||
RCT_EXPORT_METHOD(startOperationBatch)
|
||||
{
|
||||
// TODO T71377585
|
||||
}
|
||||
RCT_EXPORT_METHOD(startOperationBatch) {}
|
||||
|
||||
RCT_EXPORT_METHOD(finishOperationBatch)
|
||||
{
|
||||
// TODO T71377585
|
||||
// This method is only called from JS when animatedShouldSignalBatch is enabled.
|
||||
[self flushOperationQueues];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(createAnimatedNode : (double)tag config : (NSDictionary<NSString *, id> *)config)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager createAnimatedNode:[NSNumber numberWithDouble:tag] config:config];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager createAnimatedNode:[NSNumber numberWithDouble:tag] config:config];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(updateAnimatedNodeConfig : (double)tag config : (NSDictionary<NSString *, id> *)config)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager updateAnimatedNodeConfig:[NSNumber numberWithDouble:tag] config:config];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager updateAnimatedNodeConfig:[NSNumber numberWithDouble:tag] config:config];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(connectAnimatedNodes : (double)parentTag childTag : (double)childTag)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager connectAnimatedNodes:[NSNumber numberWithDouble:parentTag]
|
||||
childTag:[NSNumber numberWithDouble:childTag]];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager connectAnimatedNodes:[NSNumber numberWithDouble:parentTag]
|
||||
childTag:[NSNumber numberWithDouble:childTag]];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(disconnectAnimatedNodes : (double)parentTag childTag : (double)childTag)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager disconnectAnimatedNodes:[NSNumber numberWithDouble:parentTag]
|
||||
childTag:[NSNumber numberWithDouble:childTag]];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager disconnectAnimatedNodes:[NSNumber numberWithDouble:parentTag]
|
||||
childTag:[NSNumber numberWithDouble:childTag]];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(startAnimatingNode
|
||||
@@ -127,126 +118,94 @@ RCT_EXPORT_METHOD(startAnimatingNode
|
||||
: (NSDictionary<NSString *, id> *)config endCallback
|
||||
: (RCTResponseSenderBlock)callBack)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager startAnimatingNode:[NSNumber numberWithDouble:animationId]
|
||||
nodeTag:[NSNumber numberWithDouble:nodeTag]
|
||||
config:config
|
||||
endCallback:callBack];
|
||||
}];
|
||||
|
||||
[self flushOperationQueues];
|
||||
});
|
||||
[self queueFlushedOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager startAnimatingNode:[NSNumber numberWithDouble:animationId]
|
||||
nodeTag:[NSNumber numberWithDouble:nodeTag]
|
||||
config:config
|
||||
endCallback:callBack];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(stopAnimation : (double)animationId)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager stopAnimation:[NSNumber numberWithDouble:animationId]];
|
||||
}];
|
||||
[self flushOperationQueues];
|
||||
});
|
||||
[self queueFlushedOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager stopAnimation:[NSNumber numberWithDouble:animationId]];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setAnimatedNodeValue : (double)nodeTag value : (double)value)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager setAnimatedNodeValue:[NSNumber numberWithDouble:nodeTag] value:[NSNumber numberWithDouble:value]];
|
||||
}];
|
||||
// In Bridge, flushing of native animations is done from RCTCxxBridge batchDidComplete().
|
||||
// Since RCTCxxBridge doesn't exist in Bridgeless, and components are not remounted in Fabric for native animations,
|
||||
// flush here for changes in Animated.Value for Animated.event.
|
||||
[self flushOperationQueues];
|
||||
});
|
||||
[self queueFlushedOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager setAnimatedNodeValue:[NSNumber numberWithDouble:nodeTag] value:[NSNumber numberWithDouble:value]];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setAnimatedNodeOffset : (double)nodeTag offset : (double)offset)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager setAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]
|
||||
offset:[NSNumber numberWithDouble:offset]];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager setAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag] offset:[NSNumber numberWithDouble:offset]];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(flattenAnimatedNodeOffset : (double)nodeTag)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager flattenAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager flattenAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(extractAnimatedNodeOffset : (double)nodeTag)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager extractAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager extractAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(connectAnimatedNodeToView : (double)nodeTag viewTag : (double)viewTag)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
// viewName is not used when node is managed by Fabric, and nodes are always managed by Fabric in Bridgeless.
|
||||
[nodesManager connectAnimatedNodeToView:[NSNumber numberWithDouble:nodeTag]
|
||||
viewTag:[NSNumber numberWithDouble:viewTag]
|
||||
viewName:nil];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
// viewName is not used when node is managed by Fabric, and nodes are always managed by Fabric in Bridgeless.
|
||||
[nodesManager connectAnimatedNodeToView:[NSNumber numberWithDouble:nodeTag]
|
||||
viewTag:[NSNumber numberWithDouble:viewTag]
|
||||
viewName:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView : (double)nodeTag viewTag : (double)viewTag)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager disconnectAnimatedNodeFromView:[NSNumber numberWithDouble:nodeTag]
|
||||
viewTag:[NSNumber numberWithDouble:viewTag]];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager disconnectAnimatedNodeFromView:[NSNumber numberWithDouble:nodeTag]
|
||||
viewTag:[NSNumber numberWithDouble:viewTag]];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(restoreDefaultValues : (double)nodeTag)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addPreOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager restoreDefaultValues:[NSNumber numberWithDouble:nodeTag]];
|
||||
}];
|
||||
});
|
||||
[self queuePreOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager restoreDefaultValues:[NSNumber numberWithDouble:nodeTag]];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(dropAnimatedNode : (double)tag)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager dropAnimatedNode:[NSNumber numberWithDouble:tag]];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager dropAnimatedNode:[NSNumber numberWithDouble:tag]];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(startListeningToAnimatedNodeValue : (double)tag)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
__weak id<RCTValueAnimatedNodeObserver> valueObserver = self;
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager startListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag] valueObserver:valueObserver];
|
||||
}];
|
||||
});
|
||||
__weak id<RCTValueAnimatedNodeObserver> valueObserver = self;
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager startListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag] valueObserver:valueObserver];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue : (double)tag)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager stopListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag]];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager stopListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag]];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(addAnimatedEventToView
|
||||
@@ -261,13 +220,11 @@ RCT_EXPORT_METHOD(addAnimatedEventToView
|
||||
eventMappingDict[@"animatedValueTag"] = @(*eventMapping.animatedValueTag());
|
||||
}
|
||||
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager addAnimatedEventToView:[NSNumber numberWithDouble:viewTag]
|
||||
eventName:eventName
|
||||
eventMapping:eventMappingDict];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager addAnimatedEventToView:[NSNumber numberWithDouble:viewTag]
|
||||
eventName:eventName
|
||||
eventMapping:eventMappingDict];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(removeAnimatedEventFromView
|
||||
@@ -275,22 +232,18 @@ RCT_EXPORT_METHOD(removeAnimatedEventFromView
|
||||
: (nonnull NSString *)eventName animatedNodeTag
|
||||
: (double)animatedNodeTag)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager removeAnimatedEventFromView:[NSNumber numberWithDouble:viewTag]
|
||||
eventName:eventName
|
||||
animatedNodeTag:[NSNumber numberWithDouble:animatedNodeTag]];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager removeAnimatedEventFromView:[NSNumber numberWithDouble:viewTag]
|
||||
eventName:eventName
|
||||
animatedNodeTag:[NSNumber numberWithDouble:animatedNodeTag]];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getValue : (double)nodeTag saveValueCallback : (RCTResponseSenderBlock)saveValueCallback)
|
||||
{
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager getValue:[NSNumber numberWithDouble:nodeTag] saveCallback:saveValueCallback];
|
||||
}];
|
||||
});
|
||||
[self queueOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager getValue:[NSNumber numberWithDouble:nodeTag] saveCallback:saveValueCallback];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(queueAndExecuteBatchedOperations : (NSArray *)operationsAndArgs)
|
||||
@@ -300,6 +253,43 @@ RCT_EXPORT_METHOD(queueAndExecuteBatchedOperations : (NSArray *)operationsAndArg
|
||||
|
||||
#pragma mark-- Batch handling
|
||||
|
||||
- (void)queueFlushedOperationBlock:(AnimatedOperation)operation
|
||||
{
|
||||
if (facebook::react::ReactNativeFeatureFlags::animatedShouldSignalBatch()) {
|
||||
[self addOperationBlock:operation];
|
||||
} else {
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:operation];
|
||||
// In Bridge, flushing of native animations is done from RCTCxxBridge batchDidComplete().
|
||||
// Since RCTCxxBridge doesn't exist in Bridgeless, and components are not remounted in Fabric for native
|
||||
// animations, flush here for changes in Animated.Value for Animated.event.
|
||||
[self flushOperationQueues];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)queueOperationBlock:(AnimatedOperation)operation
|
||||
{
|
||||
if (facebook::react::ReactNativeFeatureFlags::animatedShouldSignalBatch()) {
|
||||
[self addOperationBlock:operation];
|
||||
} else {
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addOperationBlock:operation];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)queuePreOperationBlock:(AnimatedOperation)operation
|
||||
{
|
||||
if (facebook::react::ReactNativeFeatureFlags::animatedShouldSignalBatch()) {
|
||||
[self addPreOperationBlock:operation];
|
||||
} else {
|
||||
dispatch_async(RCTGetUIManagerQueue(), ^{
|
||||
[self addPreOperationBlock:operation];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addOperationBlock:(AnimatedOperation)operation
|
||||
{
|
||||
[_operations addObject:operation];
|
||||
@@ -335,6 +325,10 @@ RCT_EXPORT_METHOD(queueAndExecuteBatchedOperations : (NSArray *)operationsAndArg
|
||||
|
||||
- (void)willMountComponentsWithRootTag:(NSInteger)rootTag
|
||||
{
|
||||
if (facebook::react::ReactNativeFeatureFlags::animatedShouldSignalBatch()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RCTAssertMainQueue();
|
||||
RCTExecuteOnUIManagerQueue(^{
|
||||
NSArray<AnimatedOperation> *preOperations = self->_preOperations;
|
||||
@@ -350,6 +344,10 @@ RCT_EXPORT_METHOD(queueAndExecuteBatchedOperations : (NSArray *)operationsAndArg
|
||||
|
||||
- (void)didMountComponentsWithRootTag:(NSInteger)rootTag
|
||||
{
|
||||
if (facebook::react::ReactNativeFeatureFlags::animatedShouldSignalBatch()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RCTAssertMainQueue();
|
||||
RCTExecuteOnUIManagerQueue(^{
|
||||
NSArray<AnimatedOperation> *operations = self->_operations;
|
||||
|
||||
@@ -47,6 +47,7 @@ Pod::Spec.new do |s|
|
||||
add_dependency(s, "React-RCTFBReactNativeSpec")
|
||||
add_dependency(s, "ReactCommon", :subspec => "turbomodule/core", :additional_framework_paths => ["react/nativemodule/core"])
|
||||
add_dependency(s, "React-NativeModulesApple")
|
||||
add_dependency(s, "React-featureflags")
|
||||
|
||||
add_rn_third_party_dependencies(s)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user