Loading core/java/android/view/HandlerActionQueue.java 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import com.android.internal.util.GrowingArrayUtils; import android.os.Handler; import java.util.ArrayList; /** * Class used to enqueue pending work from Views when no Handler is attached. * * @hide Exposed for test framework only. */ public class HandlerActionQueue { private HandlerAction[] mActions; private int mCount; public void post(Runnable action) { postDelayed(action, 0); } public void postDelayed(Runnable action, long delayMillis) { final HandlerAction handlerAction = new HandlerAction(action, delayMillis); synchronized (this) { if (mActions == null) { mActions = new HandlerAction[4]; } mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction); mCount++; } } public void removeCallbacks(Runnable action) { synchronized (this) { final int count = mCount; int j = 0; final HandlerAction[] actions = mActions; for (int i = 0; i < count; i++) { if (actions[i].matches(action)) { // Remove this action by overwriting it within // this loop or nulling it out later. continue; } if (j != i) { // At least one previous entry was removed, so // this one needs to move to the "new" list. actions[j] = actions[i]; } j++; } // The "new" list only has j entries. mCount = j; // Null out any remaining entries. for (; j < count; j++) { actions[j] = null; } } } public void executeActions(Handler handler) { synchronized (this) { final HandlerAction[] actions = mActions; for (int i = 0, count = mCount; i < count; i++) { final HandlerAction handlerAction = actions[i]; handler.postDelayed(handlerAction.action, handlerAction.delay); } mActions = null; mCount = 0; } } public int size() { return mCount; } public Runnable getRunnable(int index) { if (index >= mCount) { throw new IndexOutOfBoundsException(); } return mActions[index].action; } public long getDelay(int index) { if (index >= mCount) { throw new IndexOutOfBoundsException(); } return mActions[index].delay; } private static class HandlerAction { final Runnable action; final long delay; public HandlerAction(Runnable action, long delay) { this.action = action; this.delay = delay; } public boolean matches(Runnable otherAction) { return otherAction == null && action == null || action != null && action.equals(otherAction); } } } core/java/android/view/View.java +42 −13 Original line number Diff line number Diff line Loading @@ -3822,6 +3822,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static SparseArray<String> mAttributeMap; /** * Queue of pending runnables. Used to postpone calls to post() until this * view is attached and has a handler. */ private HandlerActionQueue mRunQueue; /** * @hide */ Loading Loading @@ -13009,6 +13015,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return null; } /** * Returns the queue of runnable for this view. * * @return the queue of runnables for this view */ private HandlerActionQueue getRunQueue() { if (mRunQueue == null) { mRunQueue = new HandlerActionQueue(); } return mRunQueue; } /** * Gets the view root associated with the View. * @return The view root, or null if none. Loading Loading @@ -13046,8 +13064,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().post(action); return true; } Loading Loading @@ -13075,8 +13095,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (attachInfo != null) { return attachInfo.mHandler.postDelayed(action, delayMillis); } // Assume that post will succeed later ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().postDelayed(action, delayMillis); return true; } Loading @@ -13095,8 +13117,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, attachInfo.mViewRootImpl.mChoreographer.postCallback( Choreographer.CALLBACK_ANIMATION, action, null); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); // Postpone the runnable until we know // on which thread it needs to run. getRunQueue().post(action); } } Loading @@ -13118,8 +13141,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, attachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed( Choreographer.CALLBACK_ANIMATION, action, null, delayMillis); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); // Postpone the runnable until we know // on which thread it needs to run. getRunQueue().postDelayed(action, delayMillis); } } Loading @@ -13146,8 +13170,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, attachInfo.mViewRootImpl.mChoreographer.removeCallbacks( Choreographer.CALLBACK_ANIMATION, action, null); } // Assume that post will succeed later ViewRootImpl.getRunQueue().removeCallbacks(action); getRunQueue().removeCallbacks(action); } return true; } Loading Loading @@ -14565,7 +14588,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * this view */ void dispatchAttachedToWindow(AttachInfo info, int visibility) { //System.out.println("Attached! " + this); mAttachInfo = info; if (mOverlay != null) { mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility); Loading @@ -14581,6 +14603,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mAttachInfo.mScrollContainers.add(this); mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED; } // Transfer all pending runnables. if (mRunQueue != null) { mRunQueue.executeActions(info.mHandler); mRunQueue = null; } performCollectViewAttributes(mAttachInfo, visibility); onAttachedToWindow(); Loading Loading @@ -16866,7 +16893,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Choreographer.CALLBACK_ANIMATION, what, who, Choreographer.subtractFrameDelay(delay)); } else { ViewRootImpl.getRunQueue().postDelayed(what, delay); // Postpone the runnable until we know // on which thread it needs to run. getRunQueue().postDelayed(what, delay); } } } Loading @@ -16884,7 +16913,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks( Choreographer.CALLBACK_ANIMATION, what, who); } ViewRootImpl.getRunQueue().removeCallbacks(what); getRunQueue().removeCallbacks(what); } } core/java/android/view/ViewRootImpl.java +4 −78 Original line number Diff line number Diff line Loading @@ -28,7 +28,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PointF; Loading Loading @@ -131,7 +130,7 @@ public final class ViewRootImpl implements ViewParent, */ static final int MAX_TRACKBALL_DELAY = 250; static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>(); static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>(); static boolean sFirstDrawComplete = false; Loading Loading @@ -6759,89 +6758,16 @@ public final class ViewRootImpl implements ViewParent, } } static RunQueue getRunQueue() { RunQueue rq = sRunQueues.get(); static HandlerActionQueue getRunQueue() { HandlerActionQueue rq = sRunQueues.get(); if (rq != null) { return rq; } rq = new RunQueue(); rq = new HandlerActionQueue(); sRunQueues.set(rq); return rq; } /** * The run queue is used to enqueue pending work from Views when no Handler is * attached. The work is executed during the next call to performTraversals on * the thread. * @hide */ static final class RunQueue { private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>(); void post(Runnable action) { postDelayed(action, 0); } void postDelayed(Runnable action, long delayMillis) { HandlerAction handlerAction = new HandlerAction(); handlerAction.action = action; handlerAction.delay = delayMillis; synchronized (mActions) { mActions.add(handlerAction); } } void removeCallbacks(Runnable action) { final HandlerAction handlerAction = new HandlerAction(); handlerAction.action = action; synchronized (mActions) { final ArrayList<HandlerAction> actions = mActions; while (actions.remove(handlerAction)) { // Keep going } } } void executeActions(Handler handler) { synchronized (mActions) { final ArrayList<HandlerAction> actions = mActions; final int count = actions.size(); for (int i = 0; i < count; i++) { final HandlerAction handlerAction = actions.get(i); handler.postDelayed(handlerAction.action, handlerAction.delay); } actions.clear(); } } private static class HandlerAction { Runnable action; long delay; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HandlerAction that = (HandlerAction) o; return !(action != null ? !action.equals(that.action) : that.action != null); } @Override public int hashCode() { int result = action != null ? action.hashCode() : 0; result = 31 * result + (int) (delay ^ (delay >>> 32)); return result; } } } /** * Class for managing the accessibility interaction connection * based on the global accessibility state. Loading core/tests/coretests/src/android/view/HandlerActionQueueTest.java 0 → 100644 +80 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; public class HandlerActionQueueTest extends AndroidTestCase { @SmallTest public void testPostAndRemove() { HandlerActionQueue runQueue = new HandlerActionQueue(); MockRunnable runnable1 = new MockRunnable(); MockRunnable runnable2 = new MockRunnable(); MockRunnable runnable3 = new MockRunnable(); runQueue.post(runnable1); runQueue.post(runnable1); runQueue.post(runnable2); runQueue.postDelayed(runnable1, 100); runQueue.postDelayed(null, 500); assertEquals(5, runQueue.size()); assertEquals(0, runQueue.getDelay(0)); assertEquals(0, runQueue.getDelay(1)); assertEquals(0, runQueue.getDelay(2)); assertEquals(100, runQueue.getDelay(3)); assertEquals(500, runQueue.getDelay(4)); assertEquals(500, runQueue.getDelay(4)); assertEquals(runnable1, runQueue.getRunnable(0)); assertEquals(runnable1, runQueue.getRunnable(1)); assertEquals(runnable2, runQueue.getRunnable(2)); assertEquals(runnable1, runQueue.getRunnable(3)); assertEquals(null, runQueue.getRunnable(4)); runQueue.removeCallbacks(runnable1); assertEquals(2, runQueue.size()); assertEquals(0, runQueue.getDelay(0)); assertEquals(500, runQueue.getDelay(1)); assertEquals(runnable2, runQueue.getRunnable(0)); assertEquals(null, runQueue.getRunnable(1)); try { assertNull(runQueue.getRunnable(2)); assertFalse(true); } catch (IndexOutOfBoundsException e) { // Should throw an exception. } runQueue.removeCallbacks(runnable3); assertEquals(2, runQueue.size()); runQueue.removeCallbacks(runnable2); assertEquals(1, runQueue.size()); assertEquals(null, runQueue.getRunnable(0)); runQueue.removeCallbacks(null); assertEquals(0, runQueue.size()); } private static class MockRunnable implements Runnable { @Override public void run() { } } } Loading
core/java/android/view/HandlerActionQueue.java 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import com.android.internal.util.GrowingArrayUtils; import android.os.Handler; import java.util.ArrayList; /** * Class used to enqueue pending work from Views when no Handler is attached. * * @hide Exposed for test framework only. */ public class HandlerActionQueue { private HandlerAction[] mActions; private int mCount; public void post(Runnable action) { postDelayed(action, 0); } public void postDelayed(Runnable action, long delayMillis) { final HandlerAction handlerAction = new HandlerAction(action, delayMillis); synchronized (this) { if (mActions == null) { mActions = new HandlerAction[4]; } mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction); mCount++; } } public void removeCallbacks(Runnable action) { synchronized (this) { final int count = mCount; int j = 0; final HandlerAction[] actions = mActions; for (int i = 0; i < count; i++) { if (actions[i].matches(action)) { // Remove this action by overwriting it within // this loop or nulling it out later. continue; } if (j != i) { // At least one previous entry was removed, so // this one needs to move to the "new" list. actions[j] = actions[i]; } j++; } // The "new" list only has j entries. mCount = j; // Null out any remaining entries. for (; j < count; j++) { actions[j] = null; } } } public void executeActions(Handler handler) { synchronized (this) { final HandlerAction[] actions = mActions; for (int i = 0, count = mCount; i < count; i++) { final HandlerAction handlerAction = actions[i]; handler.postDelayed(handlerAction.action, handlerAction.delay); } mActions = null; mCount = 0; } } public int size() { return mCount; } public Runnable getRunnable(int index) { if (index >= mCount) { throw new IndexOutOfBoundsException(); } return mActions[index].action; } public long getDelay(int index) { if (index >= mCount) { throw new IndexOutOfBoundsException(); } return mActions[index].delay; } private static class HandlerAction { final Runnable action; final long delay; public HandlerAction(Runnable action, long delay) { this.action = action; this.delay = delay; } public boolean matches(Runnable otherAction) { return otherAction == null && action == null || action != null && action.equals(otherAction); } } }
core/java/android/view/View.java +42 −13 Original line number Diff line number Diff line Loading @@ -3822,6 +3822,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static SparseArray<String> mAttributeMap; /** * Queue of pending runnables. Used to postpone calls to post() until this * view is attached and has a handler. */ private HandlerActionQueue mRunQueue; /** * @hide */ Loading Loading @@ -13009,6 +13015,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return null; } /** * Returns the queue of runnable for this view. * * @return the queue of runnables for this view */ private HandlerActionQueue getRunQueue() { if (mRunQueue == null) { mRunQueue = new HandlerActionQueue(); } return mRunQueue; } /** * Gets the view root associated with the View. * @return The view root, or null if none. Loading Loading @@ -13046,8 +13064,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().post(action); return true; } Loading Loading @@ -13075,8 +13095,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (attachInfo != null) { return attachInfo.mHandler.postDelayed(action, delayMillis); } // Assume that post will succeed later ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().postDelayed(action, delayMillis); return true; } Loading @@ -13095,8 +13117,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, attachInfo.mViewRootImpl.mChoreographer.postCallback( Choreographer.CALLBACK_ANIMATION, action, null); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); // Postpone the runnable until we know // on which thread it needs to run. getRunQueue().post(action); } } Loading @@ -13118,8 +13141,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, attachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed( Choreographer.CALLBACK_ANIMATION, action, null, delayMillis); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); // Postpone the runnable until we know // on which thread it needs to run. getRunQueue().postDelayed(action, delayMillis); } } Loading @@ -13146,8 +13170,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, attachInfo.mViewRootImpl.mChoreographer.removeCallbacks( Choreographer.CALLBACK_ANIMATION, action, null); } // Assume that post will succeed later ViewRootImpl.getRunQueue().removeCallbacks(action); getRunQueue().removeCallbacks(action); } return true; } Loading Loading @@ -14565,7 +14588,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * this view */ void dispatchAttachedToWindow(AttachInfo info, int visibility) { //System.out.println("Attached! " + this); mAttachInfo = info; if (mOverlay != null) { mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility); Loading @@ -14581,6 +14603,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mAttachInfo.mScrollContainers.add(this); mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED; } // Transfer all pending runnables. if (mRunQueue != null) { mRunQueue.executeActions(info.mHandler); mRunQueue = null; } performCollectViewAttributes(mAttachInfo, visibility); onAttachedToWindow(); Loading Loading @@ -16866,7 +16893,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Choreographer.CALLBACK_ANIMATION, what, who, Choreographer.subtractFrameDelay(delay)); } else { ViewRootImpl.getRunQueue().postDelayed(what, delay); // Postpone the runnable until we know // on which thread it needs to run. getRunQueue().postDelayed(what, delay); } } } Loading @@ -16884,7 +16913,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks( Choreographer.CALLBACK_ANIMATION, what, who); } ViewRootImpl.getRunQueue().removeCallbacks(what); getRunQueue().removeCallbacks(what); } }
core/java/android/view/ViewRootImpl.java +4 −78 Original line number Diff line number Diff line Loading @@ -28,7 +28,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PointF; Loading Loading @@ -131,7 +130,7 @@ public final class ViewRootImpl implements ViewParent, */ static final int MAX_TRACKBALL_DELAY = 250; static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>(); static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>(); static boolean sFirstDrawComplete = false; Loading Loading @@ -6759,89 +6758,16 @@ public final class ViewRootImpl implements ViewParent, } } static RunQueue getRunQueue() { RunQueue rq = sRunQueues.get(); static HandlerActionQueue getRunQueue() { HandlerActionQueue rq = sRunQueues.get(); if (rq != null) { return rq; } rq = new RunQueue(); rq = new HandlerActionQueue(); sRunQueues.set(rq); return rq; } /** * The run queue is used to enqueue pending work from Views when no Handler is * attached. The work is executed during the next call to performTraversals on * the thread. * @hide */ static final class RunQueue { private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>(); void post(Runnable action) { postDelayed(action, 0); } void postDelayed(Runnable action, long delayMillis) { HandlerAction handlerAction = new HandlerAction(); handlerAction.action = action; handlerAction.delay = delayMillis; synchronized (mActions) { mActions.add(handlerAction); } } void removeCallbacks(Runnable action) { final HandlerAction handlerAction = new HandlerAction(); handlerAction.action = action; synchronized (mActions) { final ArrayList<HandlerAction> actions = mActions; while (actions.remove(handlerAction)) { // Keep going } } } void executeActions(Handler handler) { synchronized (mActions) { final ArrayList<HandlerAction> actions = mActions; final int count = actions.size(); for (int i = 0; i < count; i++) { final HandlerAction handlerAction = actions.get(i); handler.postDelayed(handlerAction.action, handlerAction.delay); } actions.clear(); } } private static class HandlerAction { Runnable action; long delay; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HandlerAction that = (HandlerAction) o; return !(action != null ? !action.equals(that.action) : that.action != null); } @Override public int hashCode() { int result = action != null ? action.hashCode() : 0; result = 31 * result + (int) (delay ^ (delay >>> 32)); return result; } } } /** * Class for managing the accessibility interaction connection * based on the global accessibility state. Loading
core/tests/coretests/src/android/view/HandlerActionQueueTest.java 0 → 100644 +80 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; public class HandlerActionQueueTest extends AndroidTestCase { @SmallTest public void testPostAndRemove() { HandlerActionQueue runQueue = new HandlerActionQueue(); MockRunnable runnable1 = new MockRunnable(); MockRunnable runnable2 = new MockRunnable(); MockRunnable runnable3 = new MockRunnable(); runQueue.post(runnable1); runQueue.post(runnable1); runQueue.post(runnable2); runQueue.postDelayed(runnable1, 100); runQueue.postDelayed(null, 500); assertEquals(5, runQueue.size()); assertEquals(0, runQueue.getDelay(0)); assertEquals(0, runQueue.getDelay(1)); assertEquals(0, runQueue.getDelay(2)); assertEquals(100, runQueue.getDelay(3)); assertEquals(500, runQueue.getDelay(4)); assertEquals(500, runQueue.getDelay(4)); assertEquals(runnable1, runQueue.getRunnable(0)); assertEquals(runnable1, runQueue.getRunnable(1)); assertEquals(runnable2, runQueue.getRunnable(2)); assertEquals(runnable1, runQueue.getRunnable(3)); assertEquals(null, runQueue.getRunnable(4)); runQueue.removeCallbacks(runnable1); assertEquals(2, runQueue.size()); assertEquals(0, runQueue.getDelay(0)); assertEquals(500, runQueue.getDelay(1)); assertEquals(runnable2, runQueue.getRunnable(0)); assertEquals(null, runQueue.getRunnable(1)); try { assertNull(runQueue.getRunnable(2)); assertFalse(true); } catch (IndexOutOfBoundsException e) { // Should throw an exception. } runQueue.removeCallbacks(runnable3); assertEquals(2, runQueue.size()); runQueue.removeCallbacks(runnable2); assertEquals(1, runQueue.size()); assertEquals(null, runQueue.getRunnable(0)); runQueue.removeCallbacks(null); assertEquals(0, runQueue.size()); } private static class MockRunnable implements Runnable { @Override public void run() { } } }