All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.shadows.ShadowLooper Maven / Gradle / Ivy

There is a newer version: 4.14.1
Show newest version
package org.robolectric.shadows;

import static android.os.Looper.getMainLooper;
import static org.robolectric.annotation.LooperMode.Mode.LEGACY;

import android.os.Looper;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.LooperMode;
import org.robolectric.config.ConfigurationRegistry;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.Scheduler;

/**
 * The base shadow API class for controlling Loopers.
 *
 * It will delegate calls to the appropriate shadow based on the current LooperMode.
 */
@Implements(value = Looper.class, shadowPicker = ShadowLooper.Picker.class)
public abstract class ShadowLooper {

  public static void assertLooperMode(LooperMode.Mode expectedMode) {
    LooperMode.Mode looperMode = ConfigurationRegistry.get(LooperMode.Mode.class);
    if (looperMode != expectedMode) {
      throw new IllegalStateException("this action is not supported in " + looperMode + " mode.");
    }
  }

  private static ShadowLooper shadowLooper(Looper looper) {
    return Shadow.extract(looper);
  }

  /** @deprecated Use `shadowOf({@link Looper#getMainLooper()})` instead. */
  @Deprecated
  public static ShadowLooper getShadowMainLooper() {
    return shadowLooper(getMainLooper());
  }

  // TODO: should probably remove this
  public static ShadowLooper shadowMainLooper() {
    return shadowLooper(getMainLooper());
  }

  public static Looper getLooperForThread(Thread thread) {
    if (looperMode() == LEGACY) {
        return ShadowLegacyLooper.getLooperForThread(thread);
    }
    throw new UnsupportedOperationException("this action is not supported in " + looperMode() +
        " mode.");
  }

  /**
   * Should not be called directly - Robolectric internal use only.
   */
  public static void resetThreadLoopers() {
    if (looperMode() == LEGACY) {
      ShadowLegacyLooper.resetThreadLoopers();
      return;
    }
    throw new UnsupportedOperationException("this action is not supported in " + looperMode() +
        " mode.");
  }

  /**
   * Return the current {@link LooperMode}.
   */
  public static LooperMode.Mode looperMode() {
    return ConfigurationRegistry.get(LooperMode.Mode.class);
  }

  /**
   * Pauses execution of tasks posted to the ShadowLegacyLooper. This means that during tests, tasks
   * sent to the looper will not execute immediately, but will be queued in a way that is similar to
   * how a real looper works. These queued tasks must be executed explicitly by calling {@link
   * #runToEndOftasks} or a similar method, otherwise they will not run at all before your test
   * ends.
   *
   * @param looper the looper to pause
   */
  public static void pauseLooper(Looper looper) {
    shadowLooper(looper).pause();
  }

  /**
   * Puts the shadow looper in an "unpaused" state (this is the default state). This means that
   * during tests, tasks sent to the looper will execute inline, immediately, on the calling (main)
   * thread instead of being queued, in a way similar to how Guava's "DirectExecutorService" works.
   * This is likely not to be what you want: it will cause code to be potentially executed in a
   * different order than how it would execute on the device, and if you are using certain Android
   * APIs (such as view animations) that are non-reentrant, they may not work at all or do
   * unpredictable things. For more information, see this discussion.
   *
   * @param looper the looper to pause
   */
  public static void unPauseLooper(Looper looper) {
    shadowLooper(looper).unPause();
  }

  /**
   * Puts the main ShadowLegacyLooper in an "paused" state.
   *
   * @see #pauseLooper
   */
  public static void pauseMainLooper() {
    getShadowMainLooper().pause();
  }

  /**
   * Puts the main ShadowLegacyLooper in an "unpaused" state.
   *
   * @see #unPauseLooper
   */
  public static void unPauseMainLooper() {
    getShadowMainLooper().unPause();
  }

  public static void idleMainLooper() {
    getShadowMainLooper().idle();
  }

  /** @deprecated Use {@link #idleMainLooper(long, TimeUnit)}. */
  @Deprecated
  public static void idleMainLooper(long interval) {
    idleMainLooper(interval, TimeUnit.MILLISECONDS);
  }

  public static void idleMainLooper(long amount, TimeUnit unit) {
    getShadowMainLooper().idle(amount, unit);
  }

  public static void idleMainLooperConstantly(boolean shouldIdleConstantly) {
    getShadowMainLooper().idleConstantly(shouldIdleConstantly);
  }

  public static void runMainLooperOneTask() {
    getShadowMainLooper().runOneTask();
  }

  public static void runMainLooperToNextTask() {
    getShadowMainLooper().runToNextTask();
  }

  /**
   * Runs any immediately runnable tasks previously queued on the UI thread,
   * e.g. by {@link android.app.Activity#runOnUiThread(Runnable)} or
   * {@link android.os.AsyncTask#onPostExecute(Object)}.
   *
   * **Note:** calling this method does not pause or un-pause the scheduler.
   *
   * @see #runUiThreadTasksIncludingDelayedTasks
   */
  public static void runUiThreadTasks() {
    getShadowMainLooper().idle();
  }

  /**
   * Runs all runnable tasks (pending and future) that have been queued on the UI thread. Such tasks
   * may be queued by
   * e.g. {@link android.app.Activity#runOnUiThread(Runnable)} or
   * {@link android.os.AsyncTask#onPostExecute(Object)}.
   *
   * **Note:** calling this method does not pause or un-pause the scheduler, however the clock is
   * advanced as future tasks are run.
   *
   * @see #runUiThreadTasks
   */
  public static void runUiThreadTasksIncludingDelayedTasks() {
    getShadowMainLooper().runToEndOfTasks();
  }

  public abstract void quitUnchecked();

  public abstract boolean hasQuit();

  /**
   * Executes all posted tasks scheduled before or at the current time.
   */
  public abstract void idle();

  /**
   * Advances the system clock by the given time, then executes all posted tasks scheduled before or
   * at the given time.
   */
  public abstract void idleFor(long time, TimeUnit timeUnit);

  /**
   * A variant of {@link #idleFor(long, TimeUnit)} that accepts a Duration.
   */
  @SuppressWarnings("AndroidJdkLibsChecker")
  public void idleFor(Duration duration) {
    idleFor(duration.toMillis(), TimeUnit.MILLISECONDS);
  }

  /**
   * Returns true if the looper has any pending tasks scheduled to be executed before current time.
   */
  public abstract boolean isIdle();

  /** Not supported for the main Looper in {@link LooperMode.Mode.PAUSED}. */
  public abstract void unPause();

  public abstract boolean isPaused();

  /**
   * Control the paused state of the Looper.
   *
   * 

Not supported for the main Looper in {@link LooperMode.Mode.PAUSED}. */ public abstract boolean setPaused(boolean shouldPause); /** * Only supported for {@link LooperMode.Mode.PAUSED}. */ public abstract void resetScheduler(); /** * Causes all enqueued tasks to be discarded, and pause state to be reset */ public abstract void reset(); /** * Returns the {@link org.robolectric.util.Scheduler} that is being used to manage the enqueued * tasks. This scheduler is managed by the Looper's associated queue. * * Only supported for {@link LooperMode.Mode.PAUSED}. * * @return the {@link org.robolectric.util.Scheduler} that is being used to manage the enqueued * tasks. */ public abstract Scheduler getScheduler(); /** * Runs the current task with the looper paused. * * When LooperMode is PAUSED, this will execute all pending tasks scheduled before the current * time. */ public abstract void runPaused(Runnable run); /** * Helper method to selectively call idle() only if LooperMode is PAUSED. * * Intended for backwards compatibility, to avoid changing behavior for tests still using LEGACY * LooperMode. */ public abstract void idleIfPaused(); /** * Causes {@link Runnable}s that have been scheduled to run within the next {@code intervalMillis} * milliseconds to run while advancing the scheduler's clock. * * @deprecated Use {@link #idle(long, TimeUnit)}. */ @Deprecated public void idle(long intervalMillis) { idleFor(intervalMillis, TimeUnit.MILLISECONDS); } /** * Causes {@link Runnable}s that have been scheduled to run within the next specified amount of * time to run while advancing the clock. * * @deprecated use {@link idleFor(amount, unit)} */ @Deprecated public void idle(long amount, TimeUnit unit) { idleFor(amount, unit); } public abstract void idleConstantly(boolean shouldIdleConstantly); /** * Causes all of the {@link Runnable}s that have been scheduled to run while advancing the clock to the * start time of the last scheduled {@link Runnable}. */ public abstract void runToEndOfTasks(); /** * Causes the next {@link Runnable}(s) that have been scheduled to run while advancing the clock * to its start time. If more than one {@link Runnable} is scheduled to run at this time then * they will all be run. */ public abstract void runToNextTask(); /** * Causes only one of the next {@link Runnable}s that have been scheduled to run while advancing * the clock to its start time. Only one {@link Runnable} will run even if more than one has been * scheduled to run at the same time. */ public abstract void runOneTask(); /** * Enqueue a task to be run later. * * @param runnable the task to be run * @param delayMillis how many milliseconds into the (virtual) future to run it * @return true if the runnable is enqueued * @see android.os.Handler#postDelayed(Runnable,long) * @deprecated Use a {@link android.os.Handler} instance to post to a looper. */ @Deprecated public abstract boolean post(Runnable runnable, long delayMillis); /** * Enqueue a task to be run ahead of all other delayed tasks. * * @param runnable the task to be run * @return true if the runnable is enqueued * @see android.os.Handler#postAtFrontOfQueue(Runnable) * @deprecated Use a {@link android.os.Handler} instance to post to a looper. */ @Deprecated public abstract boolean postAtFrontOfQueue(Runnable runnable); /** * Pause the looper. * * Has no practical effect for realistic looper, since it is always paused. */ public abstract void pause(); /** * @return the scheduled time of the next posted task; Duration.ZERO if there is no currently * scheduled task. */ public abstract Duration getNextScheduledTaskTime(); /** * @return the scheduled time of the last posted task; Duration.ZERO 0 if there is * no currently scheduled task. */ public abstract Duration getLastScheduledTaskTime(); public static class Picker extends LooperShadowPicker { public Picker() { super(ShadowLegacyLooper.class, ShadowPausedLooper.class); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy