Merge "Revert "Allow for replaceable clock within Looper""
This commit is contained in:
committed by
Android (Google) Code Review
commit
3632310093
@@ -182,7 +182,7 @@ public class Handler {
|
||||
*
|
||||
* Asynchronous messages represent interrupts or events that do not require global ordering
|
||||
* with respect to synchronous messages. Asynchronous messages are not subject to
|
||||
* the synchronization barriers introduced by {@link MessageQueue#postSyncBarrier()}.
|
||||
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
|
||||
*
|
||||
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
|
||||
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
|
||||
@@ -203,7 +203,7 @@ public class Handler {
|
||||
*
|
||||
* Asynchronous messages represent interrupts or events that do not require global ordering
|
||||
* with respect to synchronous messages. Asynchronous messages are not subject to
|
||||
* the synchronization barriers introduced by {@link MessageQueue#postSyncBarrier()}.
|
||||
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
|
||||
*
|
||||
* @param callback The callback interface in which to handle messages, or null.
|
||||
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
|
||||
@@ -212,17 +212,25 @@ public class Handler {
|
||||
* @hide
|
||||
*/
|
||||
public Handler(@Nullable Callback callback, boolean async) {
|
||||
this(getThreadLooper(), callback, async);
|
||||
}
|
||||
|
||||
private static Looper getThreadLooper() {
|
||||
final Looper looper = Looper.myLooper();
|
||||
if (looper == null) {
|
||||
throw new RuntimeException(
|
||||
"Can't create handler inside thread " + Thread.currentThread()
|
||||
+ " that has not called Looper.prepare()");
|
||||
if (FIND_POTENTIAL_LEAKS) {
|
||||
final Class<? extends Handler> klass = getClass();
|
||||
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
|
||||
(klass.getModifiers() & Modifier.STATIC) == 0) {
|
||||
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
|
||||
klass.getCanonicalName());
|
||||
}
|
||||
}
|
||||
return looper;
|
||||
|
||||
mLooper = Looper.myLooper();
|
||||
if (mLooper == null) {
|
||||
throw new RuntimeException(
|
||||
"Can't create handler inside thread " + Thread.currentThread()
|
||||
+ " that has not called Looper.prepare()");
|
||||
}
|
||||
mQueue = mLooper.mQueue;
|
||||
mCallback = callback;
|
||||
mAsynchronous = async;
|
||||
mIsShared = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,44 +257,14 @@ public class Handler {
|
||||
this(looper, callback, async, /* shared= */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the provided {@link Looper} instead of the default one and take a callback
|
||||
* interface in which to handle messages. Also set whether the handler
|
||||
* should be asynchronous.
|
||||
*
|
||||
* Handlers are synchronous by default unless this constructor is used to make
|
||||
* one that is strictly asynchronous.
|
||||
*
|
||||
* Asynchronous messages represent interrupts or events that do not require global ordering
|
||||
* with respect to synchronous messages. Asynchronous messages are not subject to
|
||||
* the synchronization barriers introduced by conditions such as display vsync.
|
||||
*
|
||||
* @param looper The looper, must not be null.
|
||||
* @param callback The callback interface in which to handle messages, or null.
|
||||
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
|
||||
* each {@link Message} that is sent to it or {@link Runnable} that is posted to
|
||||
* it.
|
||||
* @param shared Whether this Handler might be used by more than one client. A shared Handler
|
||||
* applies some extra policy, such as disallowing the removal of all messages,
|
||||
* in order to avoid one client affecting another's messages.
|
||||
* @hide
|
||||
*/
|
||||
/** @hide */
|
||||
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async,
|
||||
boolean shared) {
|
||||
if (FIND_POTENTIAL_LEAKS) {
|
||||
final Class<? extends Handler> klass = getClass();
|
||||
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass())
|
||||
&& (klass.getModifiers() & Modifier.STATIC) == 0) {
|
||||
Log.w(TAG, "The following Handler class should be static or leaks might occur: "
|
||||
+ klass.getCanonicalName());
|
||||
}
|
||||
}
|
||||
mLooper = looper;
|
||||
mQueue = looper.mQueue;
|
||||
mCallback = callback;
|
||||
mAsynchronous = async;
|
||||
mIsShared = shared;
|
||||
mClock = looper.getClock();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -724,14 +702,7 @@ public class Handler {
|
||||
if (delayMillis < 0) {
|
||||
delayMillis = 0;
|
||||
}
|
||||
// mClock should theoretically never be null, but some tests create a mock handler that
|
||||
// instantiates an instance where all members are null. Ideally we'd fix these tests to
|
||||
// not rely on this but there are quite a lot at this point, so it's easier to just keep
|
||||
// the existing behavior.
|
||||
if (mClock == null) {
|
||||
return false;
|
||||
}
|
||||
return sendMessageAtTime(msg, mClock.uptimeMillis() + delayMillis);
|
||||
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -924,7 +895,7 @@ public class Handler {
|
||||
}
|
||||
|
||||
public final void dump(@NonNull Printer pw, @NonNull String prefix) {
|
||||
pw.println(prefix + this + " @ " + mClock.uptimeMillis());
|
||||
pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
|
||||
if (mLooper == null) {
|
||||
pw.println(prefix + "looper uninitialized");
|
||||
} else {
|
||||
@@ -936,7 +907,7 @@ public class Handler {
|
||||
* @hide
|
||||
*/
|
||||
public final void dumpMine(@NonNull Printer pw, @NonNull String prefix) {
|
||||
pw.println(prefix + this + " @ " + mClock.uptimeMillis());
|
||||
pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
|
||||
if (mLooper == null) {
|
||||
pw.println(prefix + "looper uninitialized");
|
||||
} else {
|
||||
@@ -993,7 +964,6 @@ public class Handler {
|
||||
@UnsupportedAppUsage
|
||||
final Callback mCallback;
|
||||
final boolean mAsynchronous;
|
||||
final MessageQueue.Clock mClock;
|
||||
@UnsupportedAppUsage
|
||||
IMessenger mMessenger;
|
||||
|
||||
@@ -1027,9 +997,9 @@ public class Handler {
|
||||
|
||||
synchronized (this) {
|
||||
if (timeout > 0) {
|
||||
final long expirationTime = handler.mClock.uptimeMillis() + timeout;
|
||||
final long expirationTime = SystemClock.uptimeMillis() + timeout;
|
||||
while (!mDone) {
|
||||
long delay = expirationTime - handler.mClock.uptimeMillis();
|
||||
long delay = expirationTime - SystemClock.uptimeMillis();
|
||||
if (delay <= 0) {
|
||||
return false; // timeout
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ import android.util.Printer;
|
||||
import android.util.Slog;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Class used to run a message loop for a thread. Threads by default do
|
||||
* not have a message loop associated with them; to create one, call
|
||||
@@ -71,7 +69,7 @@ public final class Looper {
|
||||
|
||||
// sThreadLocal.get() will return null unless you've called prepare().
|
||||
@UnsupportedAppUsage
|
||||
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
|
||||
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
|
||||
@UnsupportedAppUsage
|
||||
private static Looper sMainLooper; // guarded by Looper.class
|
||||
private static Observer sObserver;
|
||||
@@ -79,7 +77,6 @@ public final class Looper {
|
||||
@UnsupportedAppUsage
|
||||
final MessageQueue mQueue;
|
||||
final Thread mThread;
|
||||
final MessageQueue.Clock mClock;
|
||||
private boolean mInLoop;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
@@ -194,7 +191,7 @@ public final class Looper {
|
||||
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
|
||||
}
|
||||
|
||||
final long dispatchStart = needStartTime ? me.mClock.uptimeMillis() : 0;
|
||||
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
|
||||
final long dispatchEnd;
|
||||
Object token = null;
|
||||
if (observer != null) {
|
||||
@@ -206,7 +203,7 @@ public final class Looper {
|
||||
if (observer != null) {
|
||||
observer.messageDispatched(token, msg);
|
||||
}
|
||||
dispatchEnd = needEndTime ? me.mClock.uptimeMillis() : 0;
|
||||
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
|
||||
} catch (Exception exception) {
|
||||
if (observer != null) {
|
||||
observer.dispatchingThrewException(token, msg, exception);
|
||||
@@ -328,13 +325,8 @@ public final class Looper {
|
||||
}
|
||||
|
||||
private Looper(boolean quitAllowed) {
|
||||
this(quitAllowed, SystemClock::uptimeMillis);
|
||||
}
|
||||
|
||||
private Looper(boolean quitAllowed, @NonNull MessageQueue.Clock clock) {
|
||||
mQueue = new MessageQueue(quitAllowed, Objects.requireNonNull(clock));
|
||||
mQueue = new MessageQueue(quitAllowed);
|
||||
mThread = Thread.currentThread();
|
||||
mClock = clock;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,16 +418,6 @@ public final class Looper {
|
||||
return mQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the looper's clock.
|
||||
*
|
||||
* @return The looper's clock
|
||||
* @hide
|
||||
*/
|
||||
public @NonNull MessageQueue.Clock getClock() {
|
||||
return mClock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the state of the looper for debugging purposes.
|
||||
*
|
||||
|
||||
@@ -19,7 +19,6 @@ package android.os;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.TestApi;
|
||||
import android.annotation.UptimeMillisLong;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.util.Log;
|
||||
import android.util.Printer;
|
||||
@@ -30,7 +29,6 @@ import java.io.FileDescriptor;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Low-level class holding the list of messages to be dispatched by a
|
||||
@@ -56,7 +54,6 @@ public final class MessageQueue {
|
||||
Message mMessages;
|
||||
@UnsupportedAppUsage
|
||||
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
|
||||
private final Clock mClock;
|
||||
private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
|
||||
private IdleHandler[] mPendingIdleHandlers;
|
||||
private boolean mQuitting;
|
||||
@@ -77,10 +74,9 @@ public final class MessageQueue {
|
||||
private native static boolean nativeIsPolling(long ptr);
|
||||
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
|
||||
|
||||
MessageQueue(boolean quitAllowed, @NonNull Clock clock) {
|
||||
MessageQueue(boolean quitAllowed) {
|
||||
mQuitAllowed = quitAllowed;
|
||||
mPtr = nativeInit();
|
||||
mClock = Objects.requireNonNull(clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,7 +106,7 @@ public final class MessageQueue {
|
||||
*/
|
||||
public boolean isIdle() {
|
||||
synchronized (this) {
|
||||
final long now = mClock.uptimeMillis();
|
||||
final long now = SystemClock.uptimeMillis();
|
||||
return mMessages == null || now < mMessages.when;
|
||||
}
|
||||
}
|
||||
@@ -340,7 +336,7 @@ public final class MessageQueue {
|
||||
|
||||
synchronized (this) {
|
||||
// Try to retrieve the next message. Return if found.
|
||||
final long now = mClock.uptimeMillis();
|
||||
final long now = SystemClock.uptimeMillis();
|
||||
Message prevMsg = null;
|
||||
Message msg = mMessages;
|
||||
if (msg != null && msg.target == null) {
|
||||
@@ -474,7 +470,7 @@ public final class MessageQueue {
|
||||
@UnsupportedAppUsage
|
||||
@TestApi
|
||||
public int postSyncBarrier() {
|
||||
return postSyncBarrier(mClock.uptimeMillis());
|
||||
return postSyncBarrier(SystemClock.uptimeMillis());
|
||||
}
|
||||
|
||||
private int postSyncBarrier(long when) {
|
||||
@@ -776,6 +772,41 @@ public final class MessageQueue {
|
||||
}
|
||||
}
|
||||
|
||||
void removeEqualMessages(Handler h, Runnable r, Object object) {
|
||||
if (h == null || r == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
Message p = mMessages;
|
||||
|
||||
// Remove all messages at front.
|
||||
while (p != null && p.target == h && p.callback == r
|
||||
&& (object == null || object.equals(p.obj))) {
|
||||
Message n = p.next;
|
||||
mMessages = n;
|
||||
p.recycleUnchecked();
|
||||
p = n;
|
||||
}
|
||||
|
||||
// Remove all messages after front.
|
||||
while (p != null) {
|
||||
Message n = p.next;
|
||||
if (n != null) {
|
||||
if (n.target == h && n.callback == r
|
||||
&& (object == null || object.equals(n.obj))) {
|
||||
Message nn = n.next;
|
||||
n.recycleUnchecked();
|
||||
p.next = nn;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
p = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void removeCallbacksAndMessages(Handler h, Object object) {
|
||||
if (h == null) {
|
||||
return;
|
||||
@@ -853,7 +884,7 @@ public final class MessageQueue {
|
||||
}
|
||||
|
||||
private void removeAllFutureMessagesLocked() {
|
||||
final long now = mClock.uptimeMillis();
|
||||
final long now = SystemClock.uptimeMillis();
|
||||
Message p = mMessages;
|
||||
if (p != null) {
|
||||
if (p.when > now) {
|
||||
@@ -882,7 +913,7 @@ public final class MessageQueue {
|
||||
|
||||
void dump(Printer pw, String prefix, Handler h) {
|
||||
synchronized (this) {
|
||||
long now = mClock.uptimeMillis();
|
||||
long now = SystemClock.uptimeMillis();
|
||||
int n = 0;
|
||||
for (Message msg = mMessages; msg != null; msg = msg.next) {
|
||||
if (h == null || h == msg.target) {
|
||||
@@ -911,7 +942,7 @@ public final class MessageQueue {
|
||||
* Callback interface for discovering when a thread is going to block
|
||||
* waiting for more messages.
|
||||
*/
|
||||
public interface IdleHandler {
|
||||
public static interface IdleHandler {
|
||||
/**
|
||||
* Called when the message queue has run out of messages and will now
|
||||
* wait for more. Return true to keep your idle handler active, false
|
||||
@@ -1010,17 +1041,4 @@ public final class MessageQueue {
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Time supplier for MessageQueue and the things that interact with it (e.g. {@link Looper}).
|
||||
*
|
||||
* Intentionally replaceable for testing.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface Clock {
|
||||
/** @see SystemClock#uptimeMillis */
|
||||
@UptimeMillisLong
|
||||
long uptimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,14 +48,13 @@ public class TestLooper {
|
||||
private static final Method MESSAGE_MARK_IN_USE_METHOD;
|
||||
private static final String TAG = "TestLooper";
|
||||
|
||||
private final MessageQueue.Clock mClock;
|
||||
private final Clock mClock;
|
||||
|
||||
private AutoDispatchThread mAutoDispatchThread;
|
||||
|
||||
static {
|
||||
try {
|
||||
LOOPER_CONSTRUCTOR = Looper.class.getDeclaredConstructor(Boolean.TYPE,
|
||||
MessageQueue.Clock.class);
|
||||
LOOPER_CONSTRUCTOR = Looper.class.getDeclaredConstructor(Boolean.TYPE);
|
||||
LOOPER_CONSTRUCTOR.setAccessible(true);
|
||||
THREAD_LOCAL_LOOPER_FIELD = Looper.class.getDeclaredField("sThreadLocal");
|
||||
THREAD_LOCAL_LOOPER_FIELD.setAccessible(true);
|
||||
@@ -84,15 +83,15 @@ public class TestLooper {
|
||||
* thread.
|
||||
*
|
||||
* Messages are dispatched when their {@link Message#when} is before or at {@link
|
||||
* MessageQueue.Clock#uptimeMillis()}.
|
||||
* Clock#uptimeMillis()}.
|
||||
* Use a custom clock with care. When using an offsettable clock like {@link
|
||||
* com.android.server.testutils.OffsettableClock} be sure not to double offset messages by
|
||||
* offsetting the clock and calling {@link #moveTimeForward(long)}. Instead, offset the clock
|
||||
* and call {@link #dispatchAll()}.
|
||||
*/
|
||||
public TestLooper(MessageQueue.Clock clock) {
|
||||
public TestLooper(Clock clock) {
|
||||
try {
|
||||
mLooper = LOOPER_CONSTRUCTOR.newInstance(false, clock);
|
||||
mLooper = LOOPER_CONSTRUCTOR.newInstance(false);
|
||||
|
||||
ThreadLocal<Looper> threadLocalLooper = (ThreadLocal<Looper>) THREAD_LOCAL_LOOPER_FIELD
|
||||
.get(null);
|
||||
@@ -225,6 +224,10 @@ public class TestLooper {
|
||||
return count;
|
||||
}
|
||||
|
||||
public interface Clock {
|
||||
long uptimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread used to dispatch messages when the main thread is blocked waiting for a response.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user