Implement Choreographer.removeFrameCallback()
diff --git a/robolectric-shadows/shadows-core/src/main/java/org/robolectric/shadows/ShadowChoreographer.java b/robolectric-shadows/shadows-core/src/main/java/org/robolectric/shadows/ShadowChoreographer.java
index 04625bd..51f2c02 100644
--- a/robolectric-shadows/shadows-core/src/main/java/org/robolectric/shadows/ShadowChoreographer.java
+++ b/robolectric-shadows/shadows-core/src/main/java/org/robolectric/shadows/ShadowChoreographer.java
@@ -41,7 +41,7 @@
@Implementation
public void postCallbackDelayed(int callbackType, Runnable action, Object token, long delayMillis) {
- ShadowApplication.getInstance().getForegroundThreadScheduler().postDelayed(action, delayMillis);
+ ShadowApplication.getInstance().getForegroundThreadScheduler().postDelayed(action, delayMillis, action);
}
@Implementation
@@ -51,7 +51,12 @@
public void run() {
callback.doFrame(getFrameTimeNanos());
}
- }, delayMillis);
+ }, delayMillis, callback);
+ }
+
+ @Implementation
+ public void removeFrameCallback(Choreographer.FrameCallback callback) {
+ ShadowApplication.getInstance().getForegroundThreadScheduler().removeWithToken(callback);
}
@Implementation
diff --git a/robolectric-utils/src/main/java/org/robolectric/util/Scheduler.java b/robolectric-utils/src/main/java/org/robolectric/util/Scheduler.java
index 3f773b4..f298525 100644
--- a/robolectric-utils/src/main/java/org/robolectric/util/Scheduler.java
+++ b/robolectric-utils/src/main/java/org/robolectric/util/Scheduler.java
@@ -69,7 +69,17 @@
* @param runnable Runnable to add.
*/
public synchronized void post(Runnable runnable) {
- postDelayed(runnable, 0);
+ post(runnable, null);
+ }
+
+ /**
+ * Add a runnable to the queue.
+ *
+ * @param runnable Runnable to add.
+ * @param token Token for runnable.
+ */
+ public synchronized void post(Runnable runnable, Object token) {
+ postDelayed(runnable, 0, token);
}
/**
@@ -79,10 +89,21 @@
* @param delayMillis Delay in millis.
*/
public synchronized void postDelayed(Runnable runnable, long delayMillis) {
+ postDelayed(runnable, delayMillis, null);
+ }
+
+ /**
+ * Add a runnable to the queue to be run after a delay.
+ *
+ * @param runnable Runnable to add.
+ * @param delayMillis Delay in millis.
+ * @param token Token for runnable.
+ */
+ public synchronized void postDelayed(Runnable runnable, long delayMillis, Object token) {
if ((!isConstantlyIdling && (paused || delayMillis > 0)) || Thread.currentThread() != associatedThread) {
- queueRunnableAndSort(runnable, currentTime + delayMillis);
+ queueRunnableAndSort(runnable, currentTime + delayMillis, token);
} else {
- runOrQueueRunnable(runnable, currentTime + delayMillis);
+ runOrQueueRunnable(runnable, currentTime + delayMillis, token);
}
}
@@ -92,10 +113,20 @@
* @param runnable Runnable to add.
*/
public synchronized void postAtFrontOfQueue(Runnable runnable) {
+ postAtFrontOfQueue(runnable, null);
+ }
+
+ /**
+ * Add a runnable to the head of the queue.
+ *
+ * @param runnable Runnable to add.
+ * @param token Token for runnable.
+ */
+ public synchronized void postAtFrontOfQueue(Runnable runnable, Object token) {
if (paused || Thread.currentThread() != associatedThread) {
runnables.add(0, new ScheduledRunnable(runnable, currentTime));
} else {
- runOrQueueRunnable(runnable, currentTime);
+ runOrQueueRunnable(runnable, currentTime, token);
}
}
@@ -115,6 +146,22 @@
}
/**
+ * Remove a runnable from the queue with the specified token.
+ *
+ * @param token Token to remove.
+ */
+ public synchronized void removeWithToken(Object token) {
+ if (token == null) throw new NullPointerException("Token must not be null");
+ ListIterator<ScheduledRunnable> iterator = runnables.listIterator();
+ while (iterator.hasNext()) {
+ ScheduledRunnable next = iterator.next();
+ if (next.token == token) {
+ iterator.remove();
+ }
+ }
+ }
+
+ /**
* Run all runnables in the queue.
*
* @return True if a runnable was executed.
@@ -220,9 +267,9 @@
return size() > 0 && runnables.get(0).scheduledTime <= endingTime;
}
- private void runOrQueueRunnable(Runnable runnable, long scheduledTime) {
+ private void runOrQueueRunnable(Runnable runnable, long scheduledTime, Object token) {
if (isExecutingRunnable) {
- queueRunnableAndSort(runnable, scheduledTime);
+ queueRunnableAndSort(runnable, scheduledTime, token);
return;
}
isExecutingRunnable = true;
@@ -244,18 +291,26 @@
}
}
- private void queueRunnableAndSort(Runnable runnable, long scheduledTime) {
- runnables.add(new ScheduledRunnable(runnable, scheduledTime));
+ private void queueRunnableAndSort(Runnable runnable, long scheduledTime, Object token) {
+ runnables.add(new ScheduledRunnable(runnable, scheduledTime, token));
Collections.sort(runnables);
}
private class ScheduledRunnable implements Comparable<ScheduledRunnable> {
private final Runnable runnable;
private final long scheduledTime;
+ private final Object token;
private ScheduledRunnable(Runnable runnable, long scheduledTime) {
this.runnable = runnable;
this.scheduledTime = scheduledTime;
+ token = null;
+ }
+
+ private ScheduledRunnable(Runnable runnable, long scheduledTime, Object token) {
+ this.runnable = runnable;
+ this.scheduledTime = scheduledTime;
+ this.token = token;
}
@Override
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowChoreographerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowChoreographerTest.java
index 3e01830..0f24fd6 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowChoreographerTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowChoreographerTest.java
@@ -3,10 +3,15 @@
import android.view.Choreographer;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
import org.robolectric.TestRunners;
import org.robolectric.util.TimeUtils;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
@RunWith(TestRunners.WithDefaults.class)
public class ShadowChoreographerTest {
@@ -24,6 +29,16 @@
}
@Test
+ public void removeFrameCallback_shouldRemoveCallback() {
+ Choreographer instance = ShadowChoreographer.getInstance();
+ Choreographer.FrameCallback callback = mock(Choreographer.FrameCallback.class);
+ instance.postFrameCallbackDelayed(callback, 1000);
+ instance.removeFrameCallback(callback);
+ ShadowApplication.getInstance().getForegroundThreadScheduler().advanceToLastPostedRunnable();
+ verify(callback, never()).doFrame(anyInt());
+ }
+
+ @Test
public void reset_shouldResetFrameInterval() {
ShadowChoreographer.setFrameInterval(1);
assertThat(ShadowChoreographer.getFrameInterval()).isEqualTo(1);
@@ -31,4 +46,4 @@
ShadowChoreographer.reset();
assertThat(ShadowChoreographer.getFrameInterval()).isEqualTo(10 * TimeUtils.NANOS_PER_MS);
}
-}
\ No newline at end of file
+}
diff --git a/robolectric/src/test/java/org/robolectric/util/SchedulerTest.java b/robolectric/src/test/java/org/robolectric/util/SchedulerTest.java
index 010ecf2..5299e53 100644
--- a/robolectric/src/test/java/org/robolectric/util/SchedulerTest.java
+++ b/robolectric/src/test/java/org/robolectric/util/SchedulerTest.java
@@ -140,6 +140,17 @@
}
@Test
+ public void removeWithToken_ShouldRemoveAllInstancesOfRunnableFromQueue() {
+ Object token = new Object();
+ scheduler.post(new TestRunnable(), token);
+ scheduler.post(new TestRunnable(), token);
+ scheduler.post(new TestRunnable());
+ assertThat(scheduler.size()).isEqualTo(3);
+ scheduler.removeWithToken(token);
+ assertThat(scheduler.size()).isEqualTo(1);
+ }
+
+ @Test
public void reset_shouldUnPause() throws Exception {
scheduler.pause();