react-native: LayoutAnimation crash on Android

We’ve addressed 3 crashes so far that are related to LayoutAnimation while we are using RN 59.8 release in our app and each of them is impacting our users a lot.:

  1. com.facebook.react.uimanager.IllegalViewOperationException: Trying to remove a view index above child count 0 at com.facebook.react.uimanager.NativeViewHierarchyManager.manageChildren …

  2. java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first. … at android.view.ViewGroup.addView at com.facebook.react.uimanager.ViewGroupManager.addView at com.facebook.react.uimanager.NativeViewHierarchyManager.manageChildren …

  3. java.lang.IndexOutOfBoundsException: index=28 count=27 … at android.view.ViewGroup.addView at com.facebook.react.uimanager.ViewGroupManager.addView at com.facebook.react.uimanager.NativeViewHierarchyManager.manageChildren …

We’ve also noticed there are a couple of commits trying to fix these crashes after 59.8 release and two of them are eventually merged:

  1. https://github.com/facebook/react-native/commit/20b4879dfd34716997e92bb5d89b0e4f873fef98#diff-55b8191729d605b79bc0964d365ce96f
  2. https://github.com/facebook/react-native/commit/5f027ec64d6764fbbb9813fabb373194dec79db7#diff-55b8191729d605b79bc0964d365ce96f

which are built in the patch that we are going to apply to our app as well. This patch significantly reduces the chance of the first and third crashes happening, but it’s still able to reproduce the second one, ‘java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first’.

Since it’s not easy to describe all details here, there is a collated document attached to this ticket that includes everything about the crash, e.g. stack trace, customized log and relevant code snippet.

React Native version:

59.8

Steps To Reproduce

We are using FlatList to wrap a list of cards and applying LayoutAnimation to it when state of content changes. Here is how we setup LayoutAnimation in JS code:

        update: {
            	type: LayoutAnimation.Types.linear,
            	property: LayoutAnimation.Properties.scaleXY,
            	duration: xxx,
        },
        delete: {
            	type: LayoutAnimation.Types.linear,
            	property: LayoutAnimation.Properties.opacity,
            	duration: xxx,
        },

From our scenario, the steps to reproduce are:

  1. Play the FlaList for a while which would trigger LayoutAnimation a couple of times
  2. Leave the current screen and then come back immediately
  3. Crash happens when application is trying to restore the previous state of this screen

Describe what you expected to happen: Everything should be fine, no crash happens at least.

Snack, code example, screenshot, or link to a repository: Here are some critical logs when the crash happens:

 Optimizer, addNonLayoutNode, parent = 4905, child = 4493, index = 0
 Optimizer, addNonLayoutNode, parent = 4905, child = 4493, index = 0
 Optimizer, addNonLayoutNode, parent = 4905, child = 4539, index = 1
 Optimizer, addNonLayoutNode, parent = 4905, child = 4539, index = 1
 Optimizer, addNonLayoutNode, parent = 4905, child = 4587, index = 2
 Optimizer, addNonLayoutNode, parent = 4905, child = 4587, index = 2
 Optimizer, addNonLayoutNode, parent = 4905, child = 4635, index = 3
 Optimizer, addNonLayoutNode, parent = 4905, child = 4635, index = 3
 Optimizer, addNonLayoutNode, parent = 4905, child = 4683, index = 4
 Optimizer, addNonLayoutNode, parent = 4905, child = 4683, index = 4
 Optimizer, addNonLayoutNode, parent = 4905, child = 4729, index = 5
 Optimizer, addNonLayoutNode, parent = 4905, child = 4729, index = 5
 Optimizer, addNonLayoutNode, parent = 4905, child = 4777, index = 6
 Optimizer, addNonLayoutNode, parent = 4905, child = 4777, index = 6
 Optimizer, addNonLayoutNode, parent = 4905, child = 4825, index = 7
 Optimizer, addNonLayoutNode, parent = 4905, child = 4825, index = 7
 Optimizer, addNonLayoutNode, parent = 4905, child = 4863, index = 8
 Optimizer, addNonLayoutNode, parent = 4905, child = 4863, index = 8
 Optimizer, addNonLayoutNode, parent = 4905, child = 4899, index = 9
 Optimizer, addNonLayoutNode, parent = 4905, child = 4899, index = 9

------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [], atIndices: [], removeFrom: [5], tag = 4905
 going to add view, tag = 4785, at 0
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4729, delete = true
 going to add view, tag = 4787, at 0
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4729, delete = true
 going to add view, tag = 4793, at 1
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4729, delete = true
 going to add view, tag = 4779, at 0
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [], atIndices: [], removeFrom: [5], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4777, delete = true
 going to add view, tag = 4783, at 1
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4777, delete = true
 going to add view, tag = 4795, at 2
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4777, delete = true
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [], atIndices: [], removeFrom: [5], tag = 4905
 going to add view, tag = 4803, at 0
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4825, delete = true
 going to add view, tag = 4807, at 1
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4825, delete = true
 going to add view, tag = 4813, at 2
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4825, delete = true
 going to add view, tag = 4817, at 0
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [], atIndices: [], removeFrom: [5], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4863, delete = true
 going to add view, tag = 4827, at 0
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4863, delete = true
 going to add view, tag = 4829, at 1
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4863, delete = true
 going to add view, tag = 4839, at 0
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [], atIndices: [], removeFrom: [5], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4899, delete = true
 going to add view, tag = 4845, at 1
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4899, delete = true
 going to add view, tag = 4849, at 2
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4899, delete = true

------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [1], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4493, delete = false
 going to add view, tag = 4867, at 1
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4493, delete = false
 going to add view, tag = 4877, at 0
 Optimizer, addNonLayoutNode, parent = 4905, child = 4493, index = 1
 going to add view, tag = 4883, at 1
 Optimizer, addNonLayoutNode, parent = 4905, child = 4493, index = 1

------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5029], atIndices: [3], removeFrom: [], tag = 4905
 going to add view, tag = 4729, at 5
 Optimizer, addNonLayoutNode, parent = 4905, child = 5029, index = 3
 going to add view, tag = 4777, at 6
 Optimizer, addNonLayoutNode, parent = 4905, child = 5029, index = 3

------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5069], atIndices: [5], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5069, index = 5
 Optimizer, addNonLayoutNode, parent = 4905, child = 5069, index = 5

------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5109], atIndices: [7], removeFrom: [], tag = 4905
 going to add view, tag = 4949, at 1
 Optimizer, addNonLayoutNode, parent = 4905, child = 5109, index = 7
 going to add view, tag = 4957, at 0
 Optimizer, addNonLayoutNode, parent = 4905, child = 5109, index = 7
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5147], atIndices: [8], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5147, index = 8
 remove view at 0, normalized index = 0, tag = 4425, parent tag = 4427
 going to add view, tag = 4927, at 0
 Optimizer, addNonLayoutNode, parent = 4905, child = 5147, index = 8
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5185], atIndices: [9], removeFrom: [], tag = 4905
 going to add view, tag = 4959, at 1
 Optimizer, addNonLayoutNode, parent = 4905, child = 5185, index = 9
 Optimizer, addNonLayoutNode, parent = 4905, child = 5185, index = 9
 remove view at 0, normalized index = 0, tag = 4493, parent tag = 4905
 going to add view, tag = 4493, at 1

------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5247], atIndices: [10], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5247, index = 10
 Optimizer, addNonLayoutNode, parent = 4905, child = 5247, index = 10
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5285], atIndices: [11], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5285, index = 11
 Optimizer, addNonLayoutNode, parent = 4905, child = 5285, index = 11
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5323], atIndices: [12], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5323, index = 12
 Optimizer, addNonLayoutNode, parent = 4905, child = 5323, index = 12
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5359], atIndices: [13], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5359, index = 13
 Optimizer, addNonLayoutNode, parent = 4905, child = 5359, index = 13
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5397], atIndices: [14], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5397, index = 14
 Optimizer, addNonLayoutNode, parent = 4905, child = 5397, index = 14
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5435], atIndices: [15], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5435, index = 15
 Optimizer, addNonLayoutNode, parent = 4905, child = 5435, index = 15
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5473], atIndices: [16], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5473, index = 16
 Optimizer, addNonLayoutNode, parent = 4905, child = 5473, index = 16
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5509], atIndices: [17], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5509, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5509, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5547], atIndices: [18], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5547, index = 18
 Optimizer, addNonLayoutNode, parent = 4905, child = 5547, index = 18
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5585], atIndices: [19], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5585, index = 19
 Optimizer, addNonLayoutNode, parent = 4905, child = 5585, index = 19

------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [], atIndices: [], removeFrom: [20], tag = 4905
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5623], atIndices: [20], removeFrom: [], tag = 4905
 remove view at 0, normalized index = 0, tag = 5113, parent tag = 5125
 Optimizer, addNonLayoutNode, parent = 4905, child = 5623, index = 20
 Optimizer, addNonLayoutNode, parent = 4905, child = 5623, index = 20
 remove view at 0, normalized index = 0, tag = 5149, parent tag = 5163
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5659], atIndices: [21], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5659, index = 21
 Optimizer, addNonLayoutNode, parent = 4905, child = 5659, index = 21
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5697], atIndices: [22], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5697, index = 22
 Optimizer, addNonLayoutNode, parent = 4905, child = 5697, index = 22
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5735], atIndices: [23], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5735, index = 23
 Optimizer, addNonLayoutNode, parent = 4905, child = 5735, index = 23
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [], moveTo: [], addTags: [5769], atIndices: [24], removeFrom: [], tag = 4905
 Optimizer, addNonLayoutNode, parent = 4905, child = 5769, index = 24
 Optimizer, addNonLayoutNode, parent = 4905, child = 5769, index = 24

------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [1], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4493, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4493, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 4493, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 4493, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4539, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4539, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 4539, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 4539, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4587, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4587, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 4587, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 4587, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5029, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5029, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 5029, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5029, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4635, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4635, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 4635, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 4635, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5069, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5069, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 5069, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5069, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4683, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 4683, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 4683, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 4683, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5109, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5109, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 5109, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5109, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5147, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5147, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 5147, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5147, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5185, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5185, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 5185, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5185, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5247, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5247, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 5247, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5247, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5285, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5285, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 5285, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5285, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5323, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5323, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 5323, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5323, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5359, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5359, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 5359, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5359, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5397, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5397, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 5397, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5397, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5435, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5435, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 5435, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5435, index = 17
------------------ (UIManager.manageChildren) tag: 4905, moveFrom: [0], moveTo: [17], addTags: [], atIndices: [], removeFrom: [], tag = 4905
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5473, delete = false
 Optimizer, removeNodeFromParent, parent = 4905, tag = 5473, delete = false
 Optimizer, addNonLayoutNode, parent = 4905, child = 5473, index = 17
 Optimizer, addNonLayoutNode, parent = 4905, child = 5473, index = 17

 remove view at 1, normalized index = 1, tag = 4493, parent tag = 4905
 going to add view, tag = 4493, at 17
 remove view at 0, normalized index = 0, tag = 4539, parent tag = 4905
 going to add view, tag = 4539, at 17
 remove view at 0, normalized index = 0, tag = 4587, parent tag = 4905
 going to add view, tag = 4587, at 17
 remove view at 0, normalized index = 0, tag = 5029, parent tag = 4905
 going to add view, tag = 5029, at 17
 remove view at 0, normalized index = 0, tag = 4635, parent tag = 4905
 going to add view, tag = 4635, at 17
 remove view at 0, normalized index = 0, tag = 4683, parent tag = 4905
 going to add view, tag = 5069, at 17

These logs reflect the whole flow of the crash happening. To help understanding, view 4905 is the view container of the list which is ReactHorizontalScrollContainerView on Android, and crash happens at the last line when adds view 5069, which already has a parent, into view 4905 at index 17.

The code run into this situation due to inconsistent view hierarchy state between ReactShadowNode and NativeViewHierarchyManager when there are pending deletion animations.

In UIImplementation, views will be added into the latest view hierarchy without any pending state calculation, but it does consider the pending state if the index of the view to be added is equal to or larger than the starting position of deleting items in NativeViewHierarchyManager.

Relevant code snippet in UIImplementation.manageChildren():

if (numToMove > 0) {
    Assertions.assertNotNull(moveFrom);
    Assertions.assertNotNull(moveTo);

    for(lastIndexRemoved = 0; lastIndexRemoved < numToMove; ++lastIndexRemoved) {
        i = moveFrom.getInt(lastIndexRemoved);
        indexToRemove = cssNodeToManage.getChildAt(i).getReactTag();
        viewsToAdd[lastIndexRemoved] = new ViewAtIndex(indexToRemove, moveTo.getInt(lastIndexRemoved));
        indicesToRemove[lastIndexRemoved] = i;
        tagsToRemove[lastIndexRemoved] = indexToRemove;
    }
}

if (numToAdd > 0) {
    Assertions.assertNotNull(addChildTags);
    Assertions.assertNotNull(addAtIndices);

    for(lastIndexRemoved = 0; lastIndexRemoved < numToAdd; ++lastIndexRemoved) {
        i = addChildTags.getInt(lastIndexRemoved);
        indexToRemove = addAtIndices.getInt(lastIndexRemoved);
        viewsToAdd[numToMove + lastIndexRemoved] = new ViewAtIndex(i, indexToRemove);
    }
}
for(i = 0; i < viewsToAdd.length; ++i) {
    ViewAtIndex viewAtIndex = viewsToAdd[i];
    ReactShadowNode cssNodeToAdd = this.mShadowNodeRegistry.getNode(viewAtIndex.mTag);
    if (cssNodeToAdd == null) {
        throw new IllegalViewOperationException("Trying to add unknown view tag: " + viewAtIndex.mTag);
    }

    cssNodeToManage.addChildAt(cssNodeToAdd, viewAtIndex.mIndex);
}

And code snippet in NativeViewHierarchyManager.manageChildren():

if (viewsToAdd != null) {
    for(i = 0; i < viewsToAdd.length; ++i) {
        ViewAtIndex viewAtIndex = viewsToAdd[i];
        View viewToAdd = (View)this.mTagsToViews.get(viewAtIndex.mTag);
        if (viewToAdd == null) {
            throw new IllegalViewOperationException("Trying to add unknown view tag: " + viewAtIndex.mTag + "\n detail: " + constructManageChildrenErrorMessage(viewToManage, viewManager, indicesToRemove, viewsToAdd, tagsToDelete));
        }

        int normalizedIndexToAdd = this.normalizeIndex(viewAtIndex.mIndex, pendingIndicesToDelete);
        viewManager.addView(viewToManage, viewToAdd, normalizedIndexToAdd);
    }
}

To help understanding, there is a picture shows the crash flow in the attached doc.

Issue of applying the patch to fix LayoutAnimation on Android.pdf

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 16
  • Comments: 35 (6 by maintainers)

Most upvoted comments

Looks like no one is working on this? Meanwhile, is there any good substitute to create a properly animated flat list?

I guess this issue is not the priority of FB team for now so they just leave it here, I’ve not had any update for months.

I don’t think this is adding anything new to the conversation, but we also started seeing this crash when going from RN 0.61 to 0.63. We had to remove UIManager.setLayoutAnimationEnabledExperimental(true); to avoid the crash.

@TheSavior I’ve built a test app to reproduce the crash, see here https://github.com/ydongzhu/create-react-native-app Run command ‘npx react-native run-android’ in terminal, and then tap ‘HACK’ button, you will see the crash happening.

This started to happen to us after upgrading to 0.64.0.

I’m testing my app after upgrading from RN 0.62.2 to 0.63.3 and randomly it crashes with java.lang.IndexOutOfBoundsException: index=5 count=4

The error seems to be related to this commit, where the “index adjustment fix” was removed.

Looking at the code in NativeViewHierarchyManager.java where the crash originates (line 507), is it correct that in the preceding lines (from 491) there is an index “normalization” check only if there are pending deletions? Obviously there aren’t pending deletions in my case, otherwise normalizedIndex would be <= viewToManage.getChildCount() and there wouldn’t be any crash.

https://github.com/facebook/react-native/blob/7100756bb8b0b1ac85ccaa32c0ce5c1cc70d524f/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java#L490-L507

I came across this issue as well. Right now, I solve it, by using reanimated layout animations in Android.

Hey peeps, this is still happening for us as well…

Affects thousands of users only on android

I think this affect a lot of other people as well, currently this is the only fix working for us: https://stackoverflow.com/questions/41819759/trying-to-remove-a-view-index-above-child-count-error

Thanks for sharing the investigation!

Can you edit your post to include the contents of your PDF? That will make it so that people searching for this bug in the future can be more likely to find this.

I think we are aware of a couple of long standing crashes with LayoutAnimation on Android but haven’t been able to figure out how to repro or track it down.

Do you have a small reproducible test case for this bug? That would be very helpful for us to figure out what’s happening.