2020import android .graphics .Region ;
2121
2222import java .util .ArrayList ;
23- import java .util .concurrent .CopyOnWriteArrayList ;
2423
2524/**
2625 * A view tree observer is used to register listeners that can be notified of global
3231 * for more information.
3332 */
3433public final class ViewTreeObserver {
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 ;
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 ;
4140 private ArrayList <OnDrawListener > mOnDrawListeners ;
4241
4342 private boolean mAlive = true ;
@@ -147,7 +146,7 @@ public final static class InternalInsetsInfo {
147146 * windows behind it should be placed.
148147 */
149148 public final Rect contentInsets = new Rect ();
150-
149+
151150 /**
152151 * Offsets from the frame of the window at which windows behind it
153152 * are visible.
@@ -166,13 +165,13 @@ public final static class InternalInsetsInfo {
166165 * can be touched.
167166 */
168167 public static final int TOUCHABLE_INSETS_FRAME = 0 ;
169-
168+
170169 /**
171170 * Option for {@link #setTouchableInsets(int)}: the area inside of
172171 * the content insets can be touched.
173172 */
174173 public static final int TOUCHABLE_INSETS_CONTENT = 1 ;
175-
174+
176175 /**
177176 * Option for {@link #setTouchableInsets(int)}: the area inside of
178177 * the visible insets can be touched.
@@ -195,7 +194,7 @@ public void setTouchableInsets(int val) {
195194 }
196195
197196 int mTouchableInsets ;
198-
197+
199198 void reset () {
200199 contentInsets .setEmpty ();
201200 visibleInsets .setEmpty ();
@@ -231,7 +230,7 @@ void set(InternalInsetsInfo other) {
231230 mTouchableInsets = other .mTouchableInsets ;
232231 }
233232 }
234-
233+
235234 /**
236235 * Interface definition for a callback to be invoked when layout has
237236 * completed and the client can compute its interior insets.
@@ -328,7 +327,7 @@ public void addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener)
328327 checkIsAlive ();
329328
330329 if (mOnGlobalFocusListeners == null ) {
331- mOnGlobalFocusListeners = new CopyOnWriteArrayList <OnGlobalFocusChangeListener >();
330+ mOnGlobalFocusListeners = new CopyOnWriteArray <OnGlobalFocusChangeListener >();
332331 }
333332
334333 mOnGlobalFocusListeners .add (listener );
@@ -363,7 +362,7 @@ public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
363362 checkIsAlive ();
364363
365364 if (mOnGlobalLayoutListeners == null ) {
366- mOnGlobalLayoutListeners = new CopyOnWriteArrayList <OnGlobalLayoutListener >();
365+ mOnGlobalLayoutListeners = new CopyOnWriteArray <OnGlobalLayoutListener >();
367366 }
368367
369368 mOnGlobalLayoutListeners .add (listener );
@@ -413,7 +412,7 @@ public void addOnPreDrawListener(OnPreDrawListener listener) {
413412 checkIsAlive ();
414413
415414 if (mOnPreDrawListeners == null ) {
416- mOnPreDrawListeners = new ArrayList <OnPreDrawListener >();
415+ mOnPreDrawListeners = new CopyOnWriteArray <OnPreDrawListener >();
417416 }
418417
419418 mOnPreDrawListeners .add (listener );
@@ -485,7 +484,7 @@ public void addOnScrollChangedListener(OnScrollChangedListener listener) {
485484 checkIsAlive ();
486485
487486 if (mOnScrollChangedListeners == null ) {
488- mOnScrollChangedListeners = new CopyOnWriteArrayList <OnScrollChangedListener >();
487+ mOnScrollChangedListeners = new CopyOnWriteArray <OnScrollChangedListener >();
489488 }
490489
491490 mOnScrollChangedListeners .add (listener );
@@ -519,7 +518,7 @@ public void addOnTouchModeChangeListener(OnTouchModeChangeListener listener) {
519518 checkIsAlive ();
520519
521520 if (mOnTouchModeChangeListeners == null ) {
522- mOnTouchModeChangeListeners = new CopyOnWriteArrayList <OnTouchModeChangeListener >();
521+ mOnTouchModeChangeListeners = new CopyOnWriteArray <OnTouchModeChangeListener >();
523522 }
524523
525524 mOnTouchModeChangeListeners .add (listener );
@@ -558,7 +557,7 @@ public void addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener l
558557
559558 if (mOnComputeInternalInsetsListeners == null ) {
560559 mOnComputeInternalInsetsListeners =
561- new CopyOnWriteArrayList <OnComputeInternalInsetsListener >();
560+ new CopyOnWriteArray <OnComputeInternalInsetsListener >();
562561 }
563562
564563 mOnComputeInternalInsetsListeners .add (listener );
@@ -622,10 +621,16 @@ final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
622621 // perform the dispatching. The iterator is a safe guard against listeners that
623622 // could mutate the list by calling the various add/remove methods. This prevents
624623 // the array from being modified while we iterate it.
625- final CopyOnWriteArrayList <OnGlobalFocusChangeListener > listeners = mOnGlobalFocusListeners ;
624+ final CopyOnWriteArray <OnGlobalFocusChangeListener > listeners = mOnGlobalFocusListeners ;
626625 if (listeners != null && listeners .size () > 0 ) {
627- for (OnGlobalFocusChangeListener listener : listeners ) {
628- listener .onGlobalFocusChanged (oldFocus , newFocus );
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 ();
629634 }
630635 }
631636 }
@@ -640,10 +645,16 @@ public final void dispatchOnGlobalLayout() {
640645 // perform the dispatching. The iterator is a safe guard against listeners that
641646 // could mutate the list by calling the various add/remove methods. This prevents
642647 // the array from being modified while we iterate it.
643- final CopyOnWriteArrayList <OnGlobalLayoutListener > listeners = mOnGlobalLayoutListeners ;
648+ final CopyOnWriteArray <OnGlobalLayoutListener > listeners = mOnGlobalLayoutListeners ;
644649 if (listeners != null && listeners .size () > 0 ) {
645- for (OnGlobalLayoutListener listener : listeners ) {
646- listener .onGlobalLayout ();
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 ();
647658 }
648659 }
649660 }
@@ -658,17 +669,17 @@ public final void dispatchOnGlobalLayout() {
658669 */
659670 @ SuppressWarnings ("unchecked" )
660671 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.
665672 boolean cancelDraw = false ;
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 ());
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 ();
672683 }
673684 }
674685 return cancelDraw ;
@@ -693,11 +704,17 @@ public final void dispatchOnDraw() {
693704 * @param inTouchMode True if the touch mode is now enabled, false otherwise.
694705 */
695706 final void dispatchOnTouchModeChanged (boolean inTouchMode ) {
696- final CopyOnWriteArrayList <OnTouchModeChangeListener > listeners =
707+ final CopyOnWriteArray <OnTouchModeChangeListener > listeners =
697708 mOnTouchModeChangeListeners ;
698709 if (listeners != null && listeners .size () > 0 ) {
699- for (OnTouchModeChangeListener listener : listeners ) {
700- listener .onTouchModeChanged (inTouchMode );
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 ();
701718 }
702719 }
703720 }
@@ -710,10 +727,16 @@ final void dispatchOnScrollChanged() {
710727 // perform the dispatching. The iterator is a safe guard against listeners that
711728 // could mutate the list by calling the various add/remove methods. This prevents
712729 // the array from being modified while we iterate it.
713- final CopyOnWriteArrayList <OnScrollChangedListener > listeners = mOnScrollChangedListeners ;
730+ final CopyOnWriteArray <OnScrollChangedListener > listeners = mOnScrollChangedListeners ;
714731 if (listeners != null && listeners .size () > 0 ) {
715- for (OnScrollChangedListener listener : listeners ) {
716- listener .onScrollChanged ();
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 ();
717740 }
718741 }
719742 }
@@ -722,11 +745,11 @@ final void dispatchOnScrollChanged() {
722745 * Returns whether there are listeners for computing internal insets.
723746 */
724747 final boolean hasComputeInternalInsetsListeners () {
725- final CopyOnWriteArrayList <OnComputeInternalInsetsListener > listeners =
748+ final CopyOnWriteArray <OnComputeInternalInsetsListener > listeners =
726749 mOnComputeInternalInsetsListeners ;
727750 return (listeners != null && listeners .size () > 0 );
728751 }
729-
752+
730753 /**
731754 * Calls all listeners to compute the current insets.
732755 */
@@ -735,12 +758,105 @@ final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) {
735758 // perform the dispatching. The iterator is a safe guard against listeners that
736759 // could mutate the list by calling the various add/remove methods. This prevents
737760 // the array from being modified while we iterate it.
738- final CopyOnWriteArrayList <OnComputeInternalInsetsListener > listeners =
761+ final CopyOnWriteArray <OnComputeInternalInsetsListener > listeners =
739762 mOnComputeInternalInsetsListeners ;
740763 if (listeners != null && listeners .size () > 0 ) {
741- for (OnComputeInternalInsetsListener listener : listeners ) {
742- listener .onComputeInternalInsets (inoutInfo );
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 );
743806 }
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 ();
744860 }
745861 }
746862}
0 commit comments