@@ -125,6 +125,27 @@ public void onDraw_unregistersItself_inLayoutChangeListener()
125125 assertThat (mOnDrawListeners .size ()).isEqualTo (0 );
126126 }
127127
128+ @ Test
129+ @ Config (sdk = 26 )
130+ public void onDraw_removesOnGlobalLayoutListenerAfterCleanup ()
131+ throws NoSuchFieldException , IllegalAccessException {
132+ ArrayList <OnDrawListener > mOnDrawListeners =
133+ initViewTreeObserverWithListener (testView .getViewTreeObserver ());
134+ ArrayList <?> mOnGlobalLayoutListeners =
135+ initViewTreeObserverGlobalLayoutListeners (testView .getViewTreeObserver ());
136+
137+ FirstDrawDoneListener .registerForNextDraw (testView , () -> {});
138+
139+ // onDraw registers a cleanup OnGlobalLayoutListener
140+ testView .getViewTreeObserver ().dispatchOnDraw ();
141+ assertThat (mOnGlobalLayoutListeners .size ()).isGreaterThan (0 );
142+
143+ // onGlobalLayout fires the cleanup, which removes both listeners
144+ testView .getViewTreeObserver ().dispatchOnGlobalLayout ();
145+ assertThat (mOnGlobalLayoutListeners ).isEmpty ();
146+ assertThat (mOnDrawListeners ).isEmpty ();
147+ }
148+
128149 /**
129150 * Returns ViewTreeObserver.mOnDrawListeners field through reflection. Since reflections are
130151 * employed, prefer to be used in tests with fixed API level using @Config(sdk = X).
@@ -148,6 +169,30 @@ private static ArrayList<OnDrawListener> initViewTreeObserverWithListener(ViewTr
148169 return listeners ;
149170 }
150171
172+ /**
173+ * Returns the internal ArrayList backing ViewTreeObserver.mOnGlobalLayoutListeners through
174+ * reflection. The field is a CopyOnWriteArray, so we read its mData field.
175+ */
176+ private static ArrayList <?> initViewTreeObserverGlobalLayoutListeners (ViewTreeObserver vto )
177+ throws NoSuchFieldException , IllegalAccessException {
178+ ViewTreeObserver .OnGlobalLayoutListener placeHolder = () -> {};
179+ vto .addOnGlobalLayoutListener (placeHolder );
180+ vto .removeOnGlobalLayoutListener (placeHolder );
181+
182+ Field mOnGlobalLayoutListeners =
183+ ViewTreeObserver .class .getDeclaredField ("mOnGlobalLayoutListeners" );
184+ mOnGlobalLayoutListeners .setAccessible (true );
185+ Object copyOnWriteArray = mOnGlobalLayoutListeners .get (vto );
186+ assertThat (copyOnWriteArray ).isNotNull ();
187+
188+ Field mData = copyOnWriteArray .getClass ().getDeclaredField ("mData" );
189+ mData .setAccessible (true );
190+ ArrayList <?> listeners = (ArrayList <?>) mData .get (copyOnWriteArray );
191+ assertThat (listeners ).isNotNull ();
192+ assertThat (listeners ).isEmpty ();
193+ return listeners ;
194+ }
195+
151196 /**
152197 * Simulates {@link View}'s dispatchAttachedToWindow() on API 25 using reflection.
153198 *
0 commit comments