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

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

package org.robolectric.shadows;

import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
import static android.os.Build.VERSION_CODES.KITKAT;
import static android.os.Build.VERSION_CODES.KITKAT_WATCH;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
import static org.robolectric.RuntimeEnvironment.getApiLevel;
import static org.robolectric.util.ReflectionHelpers.getField;
import static org.robolectric.util.ReflectionHelpers.setField;
import static org.robolectric.util.reflector.Reflector.reflector;

import android.os.Handler;
import android.os.Message;
import android.os.MessageQueue;
import java.util.ArrayList;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.RealObject;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowLegacyMessage.LegacyMessageReflector;
import org.robolectric.util.Logger;
import org.robolectric.util.Scheduler;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.Direct;
import org.robolectric.util.reflector.ForType;

/**
 * The shadow {@link MessageQueue} for {@link LooperMode.Mode.LEGACY}.
 *
 * 

In {@link LooperMode.Mode.LEGACY} Robolectric puts {@link android.os.Message}s into the * scheduler queue instead of sending them to be handled on a separate thread. {@link * android.os.Message}s that are scheduled to be dispatched can be triggered by calling {@link * ShadowLooper#idleMainLooper}. * * @see ShadowLooper */ @Implements(value = MessageQueue.class, isInAndroidSdk = false) public class ShadowLegacyMessageQueue extends ShadowMessageQueue { @RealObject private MessageQueue realQueue; private Scheduler scheduler; // Stub out the native peer - scheduling // is handled by the Scheduler class which is user-driven // rather than automatic. @HiddenApi @Implementation @SuppressWarnings("robolectric.ShadowReturnTypeMismatch") public static Number nativeInit() { return 1; } @HiddenApi @Implementation(minSdk = JELLY_BEAN_MR2, maxSdk = KITKAT_WATCH) public static void nativeDestroy(int ptr) { nativeDestroy((long) ptr); } @Implementation(minSdk = LOLLIPOP) protected static void nativeDestroy(long ptr) {} @HiddenApi @Implementation(minSdk = KITKAT, maxSdk = KITKAT_WATCH) public static boolean nativeIsIdling(int ptr) { return nativeIsIdling((long) ptr); } @Implementation(minSdk = LOLLIPOP, maxSdk = LOLLIPOP_MR1) protected static boolean nativeIsIdling(long ptr) { return false; } @Override public Scheduler getScheduler() { return scheduler; } @Override public void setScheduler(Scheduler scheduler) { this.scheduler = scheduler; } @Override public Message getHead() { return getField(realQueue, "mMessages"); } @Override public void setHead(Message msg) { reflector(MessageQueueReflector.class, realQueue).setMessages(msg); } @Override public void reset() { setHead(null); setField(realQueue, "mIdleHandlers", new ArrayList<>()); setField(realQueue, "mNextBarrierToken", 0); } @Implementation @SuppressWarnings("SynchronizeOnNonFinalField") protected boolean enqueueMessage(final Message msg, long when) { final boolean retval = reflector(MessageQueueReflector.class, realQueue).enqueueMessage(msg, when); if (retval) { final Runnable callback = new Runnable() { @Override public void run() { synchronized (realQueue) { Message m = getHead(); if (m == null) { return; } Message n = shadowOf(m).getNext(); if (m == msg) { setHead(n); } else { while (n != null) { if (n == msg) { n = shadowOf(n).getNext(); shadowOf(m).setNext(n); break; } m = n; n = shadowOf(m).getNext(); } } } dispatchMessage(msg); } }; shadowOf(msg).setScheduledRunnable(callback); if (when == 0) { scheduler.postAtFrontOfQueue(callback); } else { scheduler.postDelayed(callback, when - scheduler.getCurrentTime()); } } return retval; } private static void dispatchMessage(Message msg) { final Handler target = msg.getTarget(); shadowOf(msg).setNext(null); // If target is null it means the message has been removed // from the queue prior to being dispatched by the scheduler. if (target != null) { LegacyMessageReflector msgProxy = reflector(LegacyMessageReflector.class, msg); msgProxy.markInUse(); target.dispatchMessage(msg); if (getApiLevel() >= LOLLIPOP) { msgProxy.recycleUnchecked(); } else { msgProxy.recycle(); } } } @Implementation @HiddenApi protected void removeSyncBarrier(int token) { // TODO(https://github.com/robolectric/robolectric/issues/6852): workaround scheduler corruption // of message queue try { reflector(MessageQueueReflector.class, realQueue).removeSyncBarrier(token); } catch (IllegalStateException e) { Logger.warn("removeSyncBarrier failed! Could not find token %d", token); } } private static ShadowLegacyMessage shadowOf(Message actual) { return (ShadowLegacyMessage) Shadow.extract(actual); } /** Reflector interface for {@link MessageQueue}'s internals. */ @ForType(MessageQueue.class) interface MessageQueueReflector { @Direct boolean enqueueMessage(Message msg, long when); @Direct void removeSyncBarrier(int token); @Accessor("mMessages") void setMessages(Message msg); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy