Skip to content

Commit 0f8ffd8

Browse files
committed
Revert "Remove ViewTreeObserver allocations"
This reverts commit b999cc1. There was an assumption in this earlier change that observer dispatching could not be recursive - we could only ever have one iteration on the observer listener list. This assumption broke down in a specific app, and maybe in more, so reverting the change for now. We should probably find a way to accomplish the same allocation-minimizing goal without causing exceptions when violating our assumptions. Issue #6620795 [Application compatibility] Lufthansa app crashes Change-Id: I1c1f9ad329c14398feb0e74ce77e1a07111f7d1f
1 parent 43fa4c5 commit 0f8ffd8

File tree

1 file changed

+45
-161
lines changed

1 file changed

+45
-161
lines changed

core/java/android/view/ViewTreeObserver.java

Lines changed: 45 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import android.graphics.Region;
2121

2222
import java.util.ArrayList;
23+
import java.util.concurrent.CopyOnWriteArrayList;
2324

2425
/**
2526
* A view tree observer is used to register listeners that can be notified of global
@@ -31,12 +32,12 @@
3132
* for more information.
3233
*/
3334
public final class ViewTreeObserver {
34-
private CopyOnWriteArray<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
35-
private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
36-
private CopyOnWriteArray<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
37-
private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
38-
private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners;
39-
private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners;
35+
private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
36+
private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
37+
private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
38+
private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
39+
private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners;
40+
private ArrayList<OnPreDrawListener> mOnPreDrawListeners;
4041
private ArrayList<OnDrawListener> mOnDrawListeners;
4142

4243
private boolean mAlive = true;
@@ -146,7 +147,7 @@ public final static class InternalInsetsInfo {
146147
* windows behind it should be placed.
147148
*/
148149
public final Rect contentInsets = new Rect();
149-
150+
150151
/**
151152
* Offsets from the frame of the window at which windows behind it
152153
* are visible.
@@ -165,13 +166,13 @@ public final static class InternalInsetsInfo {
165166
* can be touched.
166167
*/
167168
public static final int TOUCHABLE_INSETS_FRAME = 0;
168-
169+
169170
/**
170171
* Option for {@link #setTouchableInsets(int)}: the area inside of
171172
* the content insets can be touched.
172173
*/
173174
public static final int TOUCHABLE_INSETS_CONTENT = 1;
174-
175+
175176
/**
176177
* Option for {@link #setTouchableInsets(int)}: the area inside of
177178
* the visible insets can be touched.
@@ -194,7 +195,7 @@ public void setTouchableInsets(int val) {
194195
}
195196

196197
int mTouchableInsets;
197-
198+
198199
void reset() {
199200
contentInsets.setEmpty();
200201
visibleInsets.setEmpty();
@@ -230,7 +231,7 @@ void set(InternalInsetsInfo other) {
230231
mTouchableInsets = other.mTouchableInsets;
231232
}
232233
}
233-
234+
234235
/**
235236
* Interface definition for a callback to be invoked when layout has
236237
* completed and the client can compute its interior insets.
@@ -327,7 +328,7 @@ public void addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener)
327328
checkIsAlive();
328329

329330
if (mOnGlobalFocusListeners == null) {
330-
mOnGlobalFocusListeners = new CopyOnWriteArray<OnGlobalFocusChangeListener>();
331+
mOnGlobalFocusListeners = new CopyOnWriteArrayList<OnGlobalFocusChangeListener>();
331332
}
332333

333334
mOnGlobalFocusListeners.add(listener);
@@ -362,7 +363,7 @@ public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
362363
checkIsAlive();
363364

364365
if (mOnGlobalLayoutListeners == null) {
365-
mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>();
366+
mOnGlobalLayoutListeners = new CopyOnWriteArrayList<OnGlobalLayoutListener>();
366367
}
367368

368369
mOnGlobalLayoutListeners.add(listener);
@@ -412,7 +413,7 @@ public void addOnPreDrawListener(OnPreDrawListener listener) {
412413
checkIsAlive();
413414

414415
if (mOnPreDrawListeners == null) {
415-
mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>();
416+
mOnPreDrawListeners = new ArrayList<OnPreDrawListener>();
416417
}
417418

418419
mOnPreDrawListeners.add(listener);
@@ -484,7 +485,7 @@ public void addOnScrollChangedListener(OnScrollChangedListener listener) {
484485
checkIsAlive();
485486

486487
if (mOnScrollChangedListeners == null) {
487-
mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>();
488+
mOnScrollChangedListeners = new CopyOnWriteArrayList<OnScrollChangedListener>();
488489
}
489490

490491
mOnScrollChangedListeners.add(listener);
@@ -518,7 +519,7 @@ public void addOnTouchModeChangeListener(OnTouchModeChangeListener listener) {
518519
checkIsAlive();
519520

520521
if (mOnTouchModeChangeListeners == null) {
521-
mOnTouchModeChangeListeners = new CopyOnWriteArray<OnTouchModeChangeListener>();
522+
mOnTouchModeChangeListeners = new CopyOnWriteArrayList<OnTouchModeChangeListener>();
522523
}
523524

524525
mOnTouchModeChangeListeners.add(listener);
@@ -557,7 +558,7 @@ public void addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener l
557558

558559
if (mOnComputeInternalInsetsListeners == null) {
559560
mOnComputeInternalInsetsListeners =
560-
new CopyOnWriteArray<OnComputeInternalInsetsListener>();
561+
new CopyOnWriteArrayList<OnComputeInternalInsetsListener>();
561562
}
562563

563564
mOnComputeInternalInsetsListeners.add(listener);
@@ -621,16 +622,10 @@ final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
621622
// perform the dispatching. The iterator is a safe guard against listeners that
622623
// could mutate the list by calling the various add/remove methods. This prevents
623624
// the array from being modified while we iterate it.
624-
final CopyOnWriteArray<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
625+
final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
625626
if (listeners != null && listeners.size() > 0) {
626-
CopyOnWriteArray.Access<OnGlobalFocusChangeListener> access = listeners.start();
627-
try {
628-
int count = access.size();
629-
for (int i = 0; i < count; i++) {
630-
access.get(i).onGlobalFocusChanged(oldFocus, newFocus);
631-
}
632-
} finally {
633-
listeners.end();
627+
for (OnGlobalFocusChangeListener listener : listeners) {
628+
listener.onGlobalFocusChanged(oldFocus, newFocus);
634629
}
635630
}
636631
}
@@ -645,16 +640,10 @@ public final void dispatchOnGlobalLayout() {
645640
// perform the dispatching. The iterator is a safe guard against listeners that
646641
// could mutate the list by calling the various add/remove methods. This prevents
647642
// the array from being modified while we iterate it.
648-
final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
643+
final CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
649644
if (listeners != null && listeners.size() > 0) {
650-
CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start();
651-
try {
652-
int count = access.size();
653-
for (int i = 0; i < count; i++) {
654-
access.get(i).onGlobalLayout();
655-
}
656-
} finally {
657-
listeners.end();
645+
for (OnGlobalLayoutListener listener : listeners) {
646+
listener.onGlobalLayout();
658647
}
659648
}
660649
}
@@ -669,17 +658,17 @@ public final void dispatchOnGlobalLayout() {
669658
*/
670659
@SuppressWarnings("unchecked")
671660
public final boolean dispatchOnPreDraw() {
661+
// NOTE: we *must* clone the listener list to perform the dispatching.
662+
// The clone is a safe guard against listeners that
663+
// could mutate the list by calling the various add/remove methods. This prevents
664+
// the array from being modified while we process it.
672665
boolean cancelDraw = false;
673-
final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
674-
if (listeners != null && listeners.size() > 0) {
675-
CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start();
676-
try {
677-
int count = access.size();
678-
for (int i = 0; i < count; i++) {
679-
cancelDraw |= !(access.get(i).onPreDraw());
680-
}
681-
} finally {
682-
listeners.end();
666+
if (mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0) {
667+
final ArrayList<OnPreDrawListener> listeners =
668+
(ArrayList<OnPreDrawListener>) mOnPreDrawListeners.clone();
669+
int numListeners = listeners.size();
670+
for (int i = 0; i < numListeners; ++i) {
671+
cancelDraw |= !(listeners.get(i).onPreDraw());
683672
}
684673
}
685674
return cancelDraw;
@@ -704,17 +693,11 @@ public final void dispatchOnDraw() {
704693
* @param inTouchMode True if the touch mode is now enabled, false otherwise.
705694
*/
706695
final void dispatchOnTouchModeChanged(boolean inTouchMode) {
707-
final CopyOnWriteArray<OnTouchModeChangeListener> listeners =
696+
final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners =
708697
mOnTouchModeChangeListeners;
709698
if (listeners != null && listeners.size() > 0) {
710-
CopyOnWriteArray.Access<OnTouchModeChangeListener> access = listeners.start();
711-
try {
712-
int count = access.size();
713-
for (int i = 0; i < count; i++) {
714-
access.get(i).onTouchModeChanged(inTouchMode);
715-
}
716-
} finally {
717-
listeners.end();
699+
for (OnTouchModeChangeListener listener : listeners) {
700+
listener.onTouchModeChanged(inTouchMode);
718701
}
719702
}
720703
}
@@ -727,16 +710,10 @@ final void dispatchOnScrollChanged() {
727710
// perform the dispatching. The iterator is a safe guard against listeners that
728711
// could mutate the list by calling the various add/remove methods. This prevents
729712
// the array from being modified while we iterate it.
730-
final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
713+
final CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
731714
if (listeners != null && listeners.size() > 0) {
732-
CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start();
733-
try {
734-
int count = access.size();
735-
for (int i = 0; i < count; i++) {
736-
access.get(i).onScrollChanged();
737-
}
738-
} finally {
739-
listeners.end();
715+
for (OnScrollChangedListener listener : listeners) {
716+
listener.onScrollChanged();
740717
}
741718
}
742719
}
@@ -745,11 +722,11 @@ final void dispatchOnScrollChanged() {
745722
* Returns whether there are listeners for computing internal insets.
746723
*/
747724
final boolean hasComputeInternalInsetsListeners() {
748-
final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
725+
final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners =
749726
mOnComputeInternalInsetsListeners;
750727
return (listeners != null && listeners.size() > 0);
751728
}
752-
729+
753730
/**
754731
* Calls all listeners to compute the current insets.
755732
*/
@@ -758,105 +735,12 @@ final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) {
758735
// perform the dispatching. The iterator is a safe guard against listeners that
759736
// could mutate the list by calling the various add/remove methods. This prevents
760737
// the array from being modified while we iterate it.
761-
final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
738+
final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners =
762739
mOnComputeInternalInsetsListeners;
763740
if (listeners != null && listeners.size() > 0) {
764-
CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start();
765-
try {
766-
int count = access.size();
767-
for (int i = 0; i < count; i++) {
768-
access.get(i).onComputeInternalInsets(inoutInfo);
769-
}
770-
} finally {
771-
listeners.end();
772-
}
773-
}
774-
}
775-
776-
/**
777-
* Copy on write array. This array is not thread safe, and only one loop can
778-
* iterate over this array at any given time. This class avoids allocations
779-
* until a concurrent modification happens.
780-
*
781-
* Usage:
782-
*
783-
* CopyOnWriteArray.Access<MyData> access = array.start();
784-
* try {
785-
* for (int i = 0; i < access.size(); i++) {
786-
* MyData d = access.get(i);
787-
* }
788-
* } finally {
789-
* access.end();
790-
* }
791-
*/
792-
static class CopyOnWriteArray<T> {
793-
private ArrayList<T> mData = new ArrayList<T>();
794-
private ArrayList<T> mDataCopy;
795-
796-
private final Access<T> mAccess = new Access<T>();
797-
798-
private boolean mStart;
799-
800-
static class Access<T> {
801-
private ArrayList<T> mData;
802-
private int mSize;
803-
804-
T get(int index) {
805-
return mData.get(index);
741+
for (OnComputeInternalInsetsListener listener : listeners) {
742+
listener.onComputeInternalInsets(inoutInfo);
806743
}
807-
808-
int size() {
809-
return mSize;
810-
}
811-
}
812-
813-
CopyOnWriteArray() {
814-
}
815-
816-
private ArrayList<T> getArray() {
817-
if (mStart) {
818-
if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData);
819-
return mDataCopy;
820-
}
821-
return mData;
822-
}
823-
824-
Access<T> start() {
825-
if (mStart) throw new IllegalStateException("Iteration already started");
826-
mStart = true;
827-
mDataCopy = null;
828-
mAccess.mData = mData;
829-
mAccess.mSize = mData.size();
830-
return mAccess;
831-
}
832-
833-
void end() {
834-
if (!mStart) throw new IllegalStateException("Iteration not started");
835-
mStart = false;
836-
if (mDataCopy != null) {
837-
mData = mDataCopy;
838-
}
839-
mDataCopy = null;
840-
}
841-
842-
int size() {
843-
return getArray().size();
844-
}
845-
846-
void add(T item) {
847-
getArray().add(item);
848-
}
849-
850-
void addAll(CopyOnWriteArray<T> array) {
851-
getArray().addAll(array.mData);
852-
}
853-
854-
void remove(T item) {
855-
getArray().remove(item);
856-
}
857-
858-
void clear() {
859-
getArray().clear();
860744
}
861745
}
862746
}

0 commit comments

Comments
 (0)