diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ab1943c49c6e5..692fa66070638 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -22369,7 +22369,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Used to track views that need (at least) a partial relayout at their current size * during the next traversal. */ - final List mPartialLayoutViews = new ArrayList(); + List mPartialLayoutViews = new ArrayList<>(); + + /** + * Swapped with mPartialLayoutViews during layout to avoid concurrent + * modification. Lazily assigned during ViewRootImpl layout. + */ + List mEmptyPartialLayoutViews; /** * Used to track the identity of the current drag operation. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b503e12a2d1d3..3c9310da6f8bf 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1974,6 +1974,15 @@ public final class ViewRootImpl implements ViewParent, final List partialLayoutViews = mAttachInfo.mPartialLayoutViews; final boolean didPartialLayout; if (!partialLayoutViews.isEmpty()) { + // Measurement or layout of views may result in changes to the list + // of partial-layout views. Swap in an "empty" list to prevent + // concurrent modification of the list being traversed. + if (mAttachInfo.mEmptyPartialLayoutViews == null) { + mAttachInfo.mPartialLayoutViews = new ArrayList<>(); + } else { + mAttachInfo.mPartialLayoutViews = mAttachInfo.mEmptyPartialLayoutViews; + } + final int count = partialLayoutViews.size(); mInLayout = true; for (int i = 0; i < count; i++) { @@ -1992,9 +2001,12 @@ public final class ViewRootImpl implements ViewParent, } } mInLayout = false; - partialLayoutViews.clear(); didPartialLayout = true; triggerGlobalLayoutListener = true; + + // The traversal list becomes the new empty list. + partialLayoutViews.clear(); + mAttachInfo.mEmptyPartialLayoutViews = partialLayoutViews; } else { didPartialLayout = false; }