1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.KITKAT_WATCH; 4 import static android.os.Build.VERSION_CODES.LOLLIPOP; 5 import static org.robolectric.RuntimeEnvironment.getApiLevel; 6 import static org.robolectric.shadow.api.Shadow.directlyOn; 7 import static org.robolectric.util.reflector.Reflector.reflector; 8 9 import android.os.Handler; 10 import android.os.Looper; 11 import android.os.Message; 12 import org.robolectric.annotation.HiddenApi; 13 import org.robolectric.annotation.Implementation; 14 import org.robolectric.annotation.Implements; 15 import org.robolectric.annotation.LooperMode; 16 import org.robolectric.annotation.RealObject; 17 import org.robolectric.shadow.api.Shadow; 18 import org.robolectric.util.Scheduler; 19 import org.robolectric.util.reflector.ForType; 20 21 /** 22 * The shadow {@link Message} for {@link LooperMode.Mode.LEGACY}. 23 * 24 * <p>In {@link LooperMode.Mode.LEGACY}, each Message is associated with a Runnable posted to the 25 * {@link Scheduler}. 26 * 27 * @see ShadowLooper 28 * @see ShadowLegacyMessageQueue 29 */ 30 @Implements(value = Message.class, isInAndroidSdk = false) 31 public class ShadowLegacyMessage extends ShadowMessage { 32 @RealObject 33 private Message realMessage; 34 private Runnable scheduledRunnable; 35 unschedule()36 private void unschedule() { 37 Handler target = realMessage.getTarget(); 38 39 if (target != null && scheduledRunnable != null) { 40 shadowOf(target.getLooper()).getScheduler().remove(scheduledRunnable); 41 scheduledRunnable = null; 42 } 43 } 44 45 /** 46 * Hook to unscheduled the callback when the message is recycled. 47 * Invokes {@link #unschedule()} and then calls through to the 48 * package private method {@link Message#recycleUnchecked()} 49 * on the real object. 50 */ 51 @HiddenApi 52 @Implementation(minSdk = LOLLIPOP) recycleUnchecked()53 public void recycleUnchecked() { 54 if (getApiLevel() >= LOLLIPOP) { 55 unschedule(); 56 reflector(MessageReflector.class, realMessage).recycleUnchecked(); 57 } else { 58 // provide forward compatibility with SDK 21. 59 recycle(); 60 } 61 } 62 63 /** 64 * Hook to unscheduled the callback when the message is recycled. Invokes {@link #unschedule()} 65 * and then calls through to {@link Message#recycle()} on the real object. 66 */ 67 @Implementation(maxSdk = KITKAT_WATCH) recycle()68 protected void recycle() { 69 unschedule(); 70 directlyOn(realMessage, Message.class, "recycle"); 71 } 72 73 @Override setScheduledRunnable(Runnable r)74 public void setScheduledRunnable(Runnable r) { 75 scheduledRunnable = r; 76 } 77 78 @Override getNext()79 public Message getNext() { 80 return reflector(MessageReflector.class, realMessage).getNext(); 81 } 82 83 @Override setNext(Message next)84 public void setNext(Message next) { 85 reflector(MessageReflector.class, realMessage).setNext(next); 86 } 87 shadowOf(Looper looper)88 private static ShadowLooper shadowOf(Looper looper) { 89 return Shadow.extract(looper); 90 } 91 92 /** Accessor interface for {@link Message}'s internals. */ 93 @ForType(Message.class) 94 interface LegacyMessageReflector { 95 markInUse()96 void markInUse(); 97 recycle()98 void recycle(); 99 recycleUnchecked()100 void recycleUnchecked(); 101 } 102 } 103