Skip to content

Commit 5f027ec

Browse files
Luna Weifacebook-github-bot
Luna Wei
authored andcommitted
Rearrange order of manageChildren
Summary: [General] [Fix] - Reorder operations of native view hierarchy When we update the native view hierarchy in `manageChildren` we: 1. iterate through all views we plan to remove and remove them from the native hierarchy 2. iterate through all views we plan to add and add them to the native hierarchy 3. iterate through all views we plan to delete and delete them (remove them from memory) This covers these cases: a. A view is moved from one place to another -- handled by steps 1 & 2 b. A view is added -- handled by step 2 c. A view is deleted -- handled by step 1 & 3 > Note the difference between remove and delete Everything above sounds fine! But... The important bit: **A view that is going to be deleted asynchronously (by a layout animation) is NOT removed in step 1. It is removed and deleted all in step 3.** See: https://fburl.com/ryxp626i If the reader may recall we solved a similar problem in D14529038 where we introduced the `pendingIndicesToDelete` data structure to keep track of views that were marked for deletion but had not yet been deleted. An example of an order of operations that we would've solved with D14529038 is: * we "delete" the view asynchronously (view A) in one operation * we add a view B that shares a parent with view A in subsequent operation * view A finally calls its callback after the animation is complete and removes itself from the native view hierarchy A case that D14529038 would not fix: 1. we add a view B in one operation 2. we delete a view A in the same operation asynchronously because it's in a layout animation 3. ... etc. What we must remember is that the index we use to add view B in step 1 is based on the indices assuming that all deletions are synchronous as this [comment notes](https://fburl.com/j9uillje): removals (both deletions and moveFroms) use the indices of the current order of the views and are assumed independent of each other. Whereas additions are indexed on the updated order (after deletions!) This diff re-arranges the order in how we act upon the operations to update the native view hierarchy -- similar to how UIImplementation does its operations on the shadow tree here: https://fburl.com/j9uillje By doing the removals and deletions first, we know that the addAt indices will be correct because either the view is removed from the native view hierarchy or `pendingIndicesToDelete` should be tracking an async delete already so the addAt index will be normalized Reviewed By: mdvacca Differential Revision: D15112664 fbshipit-source-id: 85d4b21211ac802183ca2f0fd28fc4437d312100
1 parent 38483d4 commit 5f027ec

File tree

1 file changed

+39
-39
lines changed

1 file changed

+39
-39
lines changed

ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java

+39-39
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,45 @@ public synchronized void manageChildren(
446446
}
447447
}
448448

449+
if (tagsToDelete != null) {
450+
for (int i = 0; i < tagsToDelete.length; i++) {
451+
int tagToDelete = tagsToDelete[i];
452+
final int indexToDelete = indicesToDelete[i];
453+
final View viewToDestroy = mTagsToViews.get(tagToDelete);
454+
if (viewToDestroy == null) {
455+
throw new IllegalViewOperationException(
456+
"Trying to destroy unknown view tag: "
457+
+ tagToDelete + "\n detail: " +
458+
constructManageChildrenErrorMessage(
459+
viewToManage,
460+
viewManager,
461+
indicesToRemove,
462+
viewsToAdd,
463+
tagsToDelete));
464+
}
465+
466+
if (mLayoutAnimationEnabled &&
467+
mLayoutAnimator.shouldAnimateLayout(viewToDestroy)) {
468+
int updatedCount = pendingIndicesToDelete.get(indexToDelete, 0) + 1;
469+
pendingIndicesToDelete.put(indexToDelete, updatedCount);
470+
mLayoutAnimator.deleteView(
471+
viewToDestroy,
472+
new LayoutAnimationListener() {
473+
@Override
474+
public void onAnimationEnd() {
475+
viewManager.removeView(viewToManage, viewToDestroy);
476+
dropView(viewToDestroy);
477+
478+
int count = pendingIndicesToDelete.get(indexToDelete, 0);
479+
pendingIndicesToDelete.put(indexToDelete, Math.max(0, count - 1));
480+
}
481+
});
482+
} else {
483+
dropView(viewToDestroy);
484+
}
485+
}
486+
}
487+
449488
if (viewsToAdd != null) {
450489
for (int i = 0; i < viewsToAdd.length; i++) {
451490
ViewAtIndex viewAtIndex = viewsToAdd[i];
@@ -465,45 +504,6 @@ public synchronized void manageChildren(
465504
viewManager.addView(viewToManage, viewToAdd, normalizedIndexToAdd);
466505
}
467506
}
468-
469-
if (tagsToDelete != null) {
470-
for (int i = 0; i < tagsToDelete.length; i++) {
471-
int tagToDelete = tagsToDelete[i];
472-
final int indexToDelete = indicesToDelete[i];
473-
final View viewToDestroy = mTagsToViews.get(tagToDelete);
474-
if (viewToDestroy == null) {
475-
throw new IllegalViewOperationException(
476-
"Trying to destroy unknown view tag: "
477-
+ tagToDelete + "\n detail: " +
478-
constructManageChildrenErrorMessage(
479-
viewToManage,
480-
viewManager,
481-
indicesToRemove,
482-
viewsToAdd,
483-
tagsToDelete));
484-
}
485-
486-
if (mLayoutAnimationEnabled &&
487-
mLayoutAnimator.shouldAnimateLayout(viewToDestroy)) {
488-
int updatedCount = pendingIndicesToDelete.get(indexToDelete, 0) + 1;
489-
pendingIndicesToDelete.put(indexToDelete, updatedCount);
490-
mLayoutAnimator.deleteView(
491-
viewToDestroy,
492-
new LayoutAnimationListener() {
493-
@Override
494-
public void onAnimationEnd() {
495-
viewManager.removeView(viewToManage, viewToDestroy);
496-
dropView(viewToDestroy);
497-
498-
int count = pendingIndicesToDelete.get(indexToDelete, 0);
499-
pendingIndicesToDelete.put(indexToDelete, Math.max(0, count - 1));
500-
}
501-
});
502-
} else {
503-
dropView(viewToDestroy);
504-
}
505-
}
506-
}
507507
}
508508

509509
private boolean arrayContains(@Nullable int[] array, int ele) {

0 commit comments

Comments
 (0)