Skip to content

Commit 6f5433f

Browse files
ryangombaFacebook Github Bot
authored andcommitted
Proper NativeAnimated node invalidation on Android
Summary: This diff attempts to fix a number of Android native animation bugs related to incomplete node invalidation, e.g. #10657 (comment). For full correctness, we should mark any node as needing update when it is: - created - updated (value nodes) - attached to new parents - detached from old parents - attached to a view (prop nodes) cc/ janicduplessis Closes #10837 Differential Revision: D4166446 fbshipit-source-id: dbf6b9aa34439e286234627791bb7fef647c8396
1 parent 617432c commit 6f5433f

1 file changed

Lines changed: 22 additions & 22 deletions

File tree

ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
/*package*/ class NativeAnimatedNodesManager implements EventDispatcherListener {
5050

5151
private final SparseArray<AnimatedNode> mAnimatedNodes = new SparseArray<>();
52-
private final ArrayList<AnimationDriver> mActiveAnimations = new ArrayList<>();
53-
private final ArrayList<AnimatedNode> mUpdatedNodes = new ArrayList<>();
52+
private final SparseArray<AnimationDriver> mActiveAnimations = new SparseArray<>();
53+
private final SparseArray<AnimatedNode> mUpdatedNodes = new SparseArray<>();
5454
private final Map<String, EventAnimationDriver> mEventDrivers = new HashMap<>();
5555
private final Map<String, Map<String, String>> mCustomEventTypes;
5656
private final UIImplementation mUIImplementation;
@@ -68,7 +68,7 @@ public NativeAnimatedNodesManager(UIManagerModule uiManager) {
6868
}
6969

7070
public boolean hasActiveAnimations() {
71-
return !mActiveAnimations.isEmpty() || !mUpdatedNodes.isEmpty();
71+
return mActiveAnimations.size() > 0 || mUpdatedNodes.size() > 0;
7272
}
7373

7474
public void createAnimatedNode(int tag, ReadableMap config) {
@@ -82,7 +82,6 @@ public void createAnimatedNode(int tag, ReadableMap config) {
8282
node = new StyleAnimatedNode(config, this);
8383
} else if ("value".equals(type)) {
8484
node = new ValueAnimatedNode(config);
85-
mUpdatedNodes.add(node);
8685
} else if ("props".equals(type)) {
8786
node = new PropsAnimatedNode(config, this);
8887
} else if ("interpolation".equals(type)) {
@@ -104,10 +103,12 @@ public void createAnimatedNode(int tag, ReadableMap config) {
104103
}
105104
node.mTag = tag;
106105
mAnimatedNodes.put(tag, node);
106+
mUpdatedNodes.put(tag, node);
107107
}
108108

109109
public void dropAnimatedNode(int tag) {
110110
mAnimatedNodes.remove(tag);
111+
mUpdatedNodes.remove(tag);
111112
}
112113

113114
public void startListeningToAnimatedNodeValue(int tag, AnimatedNodeValueListener listener) {
@@ -135,7 +136,7 @@ public void setAnimatedNodeValue(int tag, double value) {
135136
" does not exists or is not a 'value' node");
136137
}
137138
((ValueAnimatedNode) node).mValue = value;
138-
mUpdatedNodes.add(node);
139+
mUpdatedNodes.put(tag, node);
139140
}
140141

141142
public void setAnimatedNodeOffset(int tag, double offset) {
@@ -145,7 +146,7 @@ public void setAnimatedNodeOffset(int tag, double offset) {
145146
" does not exists or is not a 'value' node");
146147
}
147148
((ValueAnimatedNode) node).mOffset = offset;
148-
mUpdatedNodes.add(node);
149+
mUpdatedNodes.put(tag, node);
149150
}
150151

151152
public void flattenAnimatedNodeOffset(int tag) {
@@ -194,7 +195,7 @@ public void startAnimatingNode(
194195
animation.mId = animationId;
195196
animation.mEndCallback = endCallback;
196197
animation.mAnimatedValue = (ValueAnimatedNode) node;
197-
mActiveAnimations.add(animation);
198+
mActiveAnimations.put(animationId, animation);
198199
}
199200

200201
public void stopAnimation(int animationId) {
@@ -203,13 +204,13 @@ public void stopAnimation(int animationId) {
203204
// object map that would require additional memory just to support the use-case of stopping
204205
// an animation
205206
for (int i = 0; i < mActiveAnimations.size(); i++) {
206-
AnimationDriver animation = mActiveAnimations.get(i);
207+
AnimationDriver animation = mActiveAnimations.valueAt(i);
207208
if (animation.mId == animationId) {
208209
// Invoke animation end callback with {finished: false}
209210
WritableMap endCallbackResponse = Arguments.createMap();
210211
endCallbackResponse.putBoolean("finished", false);
211212
animation.mEndCallback.invoke(endCallbackResponse);
212-
mActiveAnimations.remove(i);
213+
mActiveAnimations.removeAt(i);
213214
return;
214215
}
215216
}
@@ -231,6 +232,7 @@ public void connectAnimatedNodes(int parentNodeTag, int childNodeTag) {
231232
" does not exists");
232233
}
233234
parentNode.addChild(childNode);
235+
mUpdatedNodes.put(childNodeTag, childNode);
234236
}
235237

236238
public void disconnectAnimatedNodes(int parentNodeTag, int childNodeTag) {
@@ -245,6 +247,7 @@ public void disconnectAnimatedNodes(int parentNodeTag, int childNodeTag) {
245247
" does not exists");
246248
}
247249
parentNode.removeChild(childNode);
250+
mUpdatedNodes.put(childNodeTag, childNode);
248251
}
249252

250253
public void connectAnimatedNodeToView(int animatedNodeTag, int viewTag) {
@@ -263,6 +266,7 @@ public void connectAnimatedNodeToView(int animatedNodeTag, int viewTag) {
263266
"already attached to a view");
264267
}
265268
propsAnimatedNode.mConnectedViewTag = viewTag;
269+
mUpdatedNodes.put(animatedNodeTag, node);
266270
}
267271

268272
public void disconnectAnimatedNodeFromView(int animatedNodeTag, int viewTag) {
@@ -327,7 +331,7 @@ public boolean onEventDispatch(Event event) {
327331
EventAnimationDriver eventDriver = mEventDrivers.get(event.getViewTag() + eventName);
328332
if (eventDriver != null) {
329333
event.dispatch(eventDriver);
330-
mUpdatedNodes.add(eventDriver.mValueNode);
334+
mUpdatedNodes.put(eventDriver.mValueNode.mTag, eventDriver.mValueNode);
331335
return true;
332336
}
333337
}
@@ -368,7 +372,7 @@ public void runUpdates(long frameTimeNanos) {
368372

369373
Queue<AnimatedNode> nodesQueue = new ArrayDeque<>();
370374
for (int i = 0; i < mUpdatedNodes.size(); i++) {
371-
AnimatedNode node = mUpdatedNodes.get(i);
375+
AnimatedNode node = mUpdatedNodes.valueAt(i);
372376
if (node.mBFSColor != mAnimatedGraphBFSColor) {
373377
node.mBFSColor = mAnimatedGraphBFSColor;
374378
activeNodesCount++;
@@ -377,7 +381,7 @@ public void runUpdates(long frameTimeNanos) {
377381
}
378382

379383
for (int i = 0; i < mActiveAnimations.size(); i++) {
380-
AnimationDriver animation = mActiveAnimations.get(i);
384+
AnimationDriver animation = mActiveAnimations.valueAt(i);
381385
animation.runAnimationStep(frameTimeNanos);
382386
AnimatedNode valueNode = animation.mAnimatedValue;
383387
if (valueNode.mBFSColor != mAnimatedGraphBFSColor) {
@@ -422,15 +426,15 @@ public void runUpdates(long frameTimeNanos) {
422426
// find nodes with zero "incoming nodes", those can be either nodes from `mUpdatedNodes` or
423427
// ones connected to active animations
424428
for (int i = 0; i < mUpdatedNodes.size(); i++) {
425-
AnimatedNode node = mUpdatedNodes.get(i);
429+
AnimatedNode node = mUpdatedNodes.valueAt(i);
426430
if (node.mActiveIncomingNodes == 0 && node.mBFSColor != mAnimatedGraphBFSColor) {
427431
node.mBFSColor = mAnimatedGraphBFSColor;
428432
updatedNodesCount++;
429433
nodesQueue.add(node);
430434
}
431435
}
432436
for (int i = 0; i < mActiveAnimations.size(); i++) {
433-
AnimationDriver animation = mActiveAnimations.get(i);
437+
AnimationDriver animation = mActiveAnimations.valueAt(i);
434438
AnimatedNode valueNode = animation.mAnimatedValue;
435439
if (valueNode.mActiveIncomingNodes == 0 && valueNode.mBFSColor != mAnimatedGraphBFSColor) {
436440
valueNode.mBFSColor = mAnimatedGraphBFSColor;
@@ -480,19 +484,15 @@ public void runUpdates(long frameTimeNanos) {
480484
// finished, then resize `mActiveAnimations`.
481485
if (hasFinishedAnimations) {
482486
int dest = 0;
483-
for (int i = 0; i < mActiveAnimations.size(); i++) {
484-
AnimationDriver animation = mActiveAnimations.get(i);
485-
if (!animation.mHasFinished) {
486-
mActiveAnimations.set(dest++, animation);
487-
} else {
487+
for (int i = mActiveAnimations.size() - 1; i >= 0; i--) {
488+
AnimationDriver animation = mActiveAnimations.valueAt(i);
489+
if (animation.mHasFinished) {
488490
WritableMap endCallbackResponse = Arguments.createMap();
489491
endCallbackResponse.putBoolean("finished", true);
490492
animation.mEndCallback.invoke(endCallbackResponse);
493+
mActiveAnimations.removeAt(i);
491494
}
492495
}
493-
for (int i = mActiveAnimations.size() - 1; i >= dest; i--) {
494-
mActiveAnimations.remove(i);
495-
}
496496
}
497497
}
498498
}

0 commit comments

Comments
 (0)