2022-10-02 23:06:56 +02:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2006 The Android Open Source Project
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package android.os;
|
|
|
|
|
|
|
|
|
|
import android.util.AndroidRuntimeException;
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
import android.util.Printer;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Low-level class holding the list of messages to be dispatched by a
|
|
|
|
|
* {@link Looper}. Messages are not added directly to a MessageQueue,
|
|
|
|
|
* but rather through {@link Handler} objects associated with the Looper.
|
2023-06-22 11:45:46 +02:00
|
|
|
*
|
2022-10-02 23:06:56 +02:00
|
|
|
* <p>You can retrieve the MessageQueue for the current thread with
|
|
|
|
|
* {@link Looper#myQueue() Looper.myQueue()}.
|
|
|
|
|
*/
|
|
|
|
|
public final class MessageQueue {
|
2023-06-22 11:45:46 +02:00
|
|
|
// True if the message queue can be quit.
|
|
|
|
|
private final boolean mQuitAllowed;
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unused")
|
2023-07-25 14:26:29 +02:00
|
|
|
private long mPtr; // used by native code
|
2023-06-22 11:45:46 +02:00
|
|
|
|
|
|
|
|
Message mMessages;
|
|
|
|
|
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
|
|
|
|
|
private IdleHandler[] mPendingIdleHandlers;
|
|
|
|
|
private boolean mQuitting;
|
|
|
|
|
|
|
|
|
|
// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
|
|
|
|
|
private boolean mBlocked;
|
|
|
|
|
|
|
|
|
|
// The next barrier token.
|
|
|
|
|
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
|
|
|
|
|
private int mNextBarrierToken;
|
|
|
|
|
|
2023-07-25 14:26:29 +02:00
|
|
|
private native static long nativeInit();
|
|
|
|
|
private native static void nativeDestroy(long ptr);
|
2023-08-08 10:16:17 +02:00
|
|
|
private native static boolean nativePollOnce(long ptr, int timeoutMillis);
|
2023-07-25 14:26:29 +02:00
|
|
|
private native static void nativeWake(long ptr);
|
|
|
|
|
private native static boolean nativeIsIdling(long ptr);
|
2023-06-22 11:45:46 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Callback interface for discovering when a thread is going to block
|
|
|
|
|
* waiting for more messages.
|
|
|
|
|
*/
|
|
|
|
|
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
|
|
|
|
|
* to have it removed. This may be called if there are still messages
|
|
|
|
|
* pending in the queue, but they are all scheduled to be dispatched
|
|
|
|
|
* after the current time.
|
|
|
|
|
*/
|
|
|
|
|
boolean queueIdle();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a new {@link IdleHandler} to this message queue. This may be
|
|
|
|
|
* removed automatically for you by returning false from
|
|
|
|
|
* {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
|
|
|
|
|
* invoked, or explicitly removing it with {@link #removeIdleHandler}.
|
|
|
|
|
*
|
|
|
|
|
* <p>This method is safe to call from any thread.
|
|
|
|
|
*
|
|
|
|
|
* @param handler The IdleHandler to be added.
|
|
|
|
|
*/
|
|
|
|
|
public void addIdleHandler(IdleHandler handler) {
|
|
|
|
|
if (handler == null) {
|
|
|
|
|
throw new NullPointerException("Can't add a null IdleHandler");
|
|
|
|
|
}
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
mIdleHandlers.add(handler);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Remove an {@link IdleHandler} from the queue that was previously added
|
|
|
|
|
* with {@link #addIdleHandler}. If the given object is not currently
|
|
|
|
|
* in the idle list, nothing is done.
|
|
|
|
|
*
|
|
|
|
|
* @param handler The IdleHandler to be removed.
|
|
|
|
|
*/
|
|
|
|
|
public void removeIdleHandler(IdleHandler handler) {
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
mIdleHandlers.remove(handler);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MessageQueue(boolean quitAllowed) {
|
|
|
|
|
mQuitAllowed = quitAllowed;
|
|
|
|
|
mPtr = nativeInit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void finalize() throws Throwable {
|
|
|
|
|
try {
|
|
|
|
|
dispose();
|
|
|
|
|
} finally {
|
|
|
|
|
super.finalize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Disposes of the underlying message queue.
|
|
|
|
|
// Must only be called on the looper thread or the finalizer.
|
|
|
|
|
private void dispose() {
|
|
|
|
|
if (mPtr != 0) {
|
|
|
|
|
nativeDestroy(mPtr);
|
|
|
|
|
mPtr = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Message next() {
|
|
|
|
|
int pendingIdleHandlerCount = -1; // -1 only during first iteration
|
|
|
|
|
int nextPollTimeoutMillis = 0;
|
|
|
|
|
for (;;) {
|
|
|
|
|
if (nextPollTimeoutMillis != 0) {
|
|
|
|
|
// Binder.flushPendingCommands();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We can assume mPtr != 0 because the loop is obviously still running.
|
|
|
|
|
// The looper will not call this method after the loop quits.
|
2023-08-08 10:16:17 +02:00
|
|
|
if (nativePollOnce(mPtr, nextPollTimeoutMillis)) {
|
|
|
|
|
return null; // thread is managed by glib, so return instead of blocking
|
|
|
|
|
}
|
2023-06-22 11:45:46 +02:00
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
// Try to retrieve the next message. Return if found.
|
|
|
|
|
final long now = SystemClock.uptimeMillis();
|
|
|
|
|
Message prevMsg = null;
|
|
|
|
|
Message msg = mMessages;
|
|
|
|
|
if (msg != null && msg.target == null) {
|
|
|
|
|
// Stalled by a barrier. Find the next asynchronous message in the queue.
|
|
|
|
|
do {
|
|
|
|
|
prevMsg = msg;
|
|
|
|
|
msg = msg.next;
|
|
|
|
|
} while (msg != null && !msg.isAsynchronous());
|
|
|
|
|
}
|
|
|
|
|
if (msg != null) {
|
|
|
|
|
if (now < msg.when) {
|
|
|
|
|
// Next message is not ready. Set a timeout to wake up when it is ready.
|
|
|
|
|
nextPollTimeoutMillis = (int)Math.min(msg.when - now, Integer.MAX_VALUE);
|
|
|
|
|
} else {
|
|
|
|
|
// Got a message.
|
|
|
|
|
mBlocked = false;
|
|
|
|
|
if (prevMsg != null) {
|
|
|
|
|
prevMsg.next = msg.next;
|
|
|
|
|
} else {
|
|
|
|
|
mMessages = msg.next;
|
|
|
|
|
}
|
|
|
|
|
msg.next = null;
|
|
|
|
|
if (false)
|
|
|
|
|
Log.v("MessageQueue", "Returning message: " + msg);
|
|
|
|
|
msg.markInUse();
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// No more messages.
|
|
|
|
|
nextPollTimeoutMillis = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process the quit message now that all pending messages have been handled.
|
|
|
|
|
if (mQuitting) {
|
|
|
|
|
dispose();
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If first time idle, then get the number of idlers to run.
|
|
|
|
|
// Idle handles only run if the queue is empty or if the first message
|
|
|
|
|
// in the queue (possibly a barrier) is due to be handled in the future.
|
|
|
|
|
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
|
|
|
|
|
pendingIdleHandlerCount = mIdleHandlers.size();
|
|
|
|
|
}
|
|
|
|
|
if (pendingIdleHandlerCount <= 0) {
|
|
|
|
|
// No idle handlers to run. Loop and wait some more.
|
|
|
|
|
mBlocked = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mPendingIdleHandlers == null) {
|
|
|
|
|
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
|
|
|
|
|
}
|
|
|
|
|
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run the idle handlers.
|
|
|
|
|
// We only ever reach this code block during the first iteration.
|
|
|
|
|
for (int i = 0; i < pendingIdleHandlerCount; i++) {
|
|
|
|
|
final IdleHandler idler = mPendingIdleHandlers[i];
|
|
|
|
|
mPendingIdleHandlers[i] = null; // release the reference to the handler
|
|
|
|
|
|
|
|
|
|
boolean keep = false;
|
|
|
|
|
try {
|
|
|
|
|
keep = idler.queueIdle();
|
|
|
|
|
} catch (Throwable t) {
|
|
|
|
|
Log.wtf("MessageQueue", "IdleHandler threw exception", t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!keep) {
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
mIdleHandlers.remove(idler);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reset the idle handler count to 0 so we do not run them again.
|
|
|
|
|
pendingIdleHandlerCount = 0;
|
|
|
|
|
|
|
|
|
|
// While calling an idle handler, a new message could have been delivered
|
|
|
|
|
// so go back and look again for a pending message without waiting.
|
|
|
|
|
nextPollTimeoutMillis = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void quit(boolean safe) {
|
|
|
|
|
if (!mQuitAllowed) {
|
|
|
|
|
throw new RuntimeException("Main thread not allowed to quit.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
if (mQuitting) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
mQuitting = true;
|
|
|
|
|
|
|
|
|
|
if (safe) {
|
|
|
|
|
removeAllFutureMessagesLocked();
|
|
|
|
|
} else {
|
|
|
|
|
removeAllMessagesLocked();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We can assume mPtr != 0 because mQuitting was previously false.
|
|
|
|
|
nativeWake(mPtr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int enqueueSyncBarrier(long when) {
|
|
|
|
|
// Enqueue a new sync barrier token.
|
|
|
|
|
// We don't need to wake the queue because the purpose of a barrier is to stall it.
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
final int token = mNextBarrierToken++;
|
|
|
|
|
final Message msg = Message.obtain();
|
|
|
|
|
msg.when = when;
|
|
|
|
|
msg.arg1 = token;
|
|
|
|
|
|
|
|
|
|
Message prev = null;
|
|
|
|
|
Message p = mMessages;
|
|
|
|
|
if (when != 0) {
|
|
|
|
|
while (p != null && p.when <= when) {
|
|
|
|
|
prev = p;
|
|
|
|
|
p = p.next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (prev != null) { // invariant: p == prev.next
|
|
|
|
|
msg.next = p;
|
|
|
|
|
prev.next = msg;
|
|
|
|
|
} else {
|
|
|
|
|
msg.next = p;
|
|
|
|
|
mMessages = msg;
|
|
|
|
|
}
|
|
|
|
|
return token;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void removeSyncBarrier(int token) {
|
|
|
|
|
// Remove a sync barrier token from the queue.
|
|
|
|
|
// If the queue is no longer stalled by a barrier then wake it.
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
Message prev = null;
|
|
|
|
|
Message p = mMessages;
|
|
|
|
|
while (p != null && (p.target != null || p.arg1 != token)) {
|
|
|
|
|
prev = p;
|
|
|
|
|
p = p.next;
|
|
|
|
|
}
|
|
|
|
|
if (p == null) {
|
|
|
|
|
throw new IllegalStateException("The specified message queue synchronization "
|
|
|
|
|
+ " barrier token has not been posted or has already been removed.");
|
|
|
|
|
}
|
|
|
|
|
final boolean needWake;
|
|
|
|
|
if (prev != null) {
|
|
|
|
|
prev.next = p.next;
|
|
|
|
|
needWake = false;
|
|
|
|
|
} else {
|
|
|
|
|
mMessages = p.next;
|
|
|
|
|
needWake = mMessages == null || mMessages.target != null;
|
|
|
|
|
}
|
|
|
|
|
p.recycle();
|
|
|
|
|
|
|
|
|
|
// If the loop is quitting then it is already awake.
|
|
|
|
|
// We can assume mPtr != 0 when mQuitting is false.
|
|
|
|
|
if (needWake && !mQuitting) {
|
|
|
|
|
nativeWake(mPtr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean enqueueMessage(Message msg, long when) {
|
|
|
|
|
if (msg.isInUse()) {
|
|
|
|
|
throw new AndroidRuntimeException(msg + " This message is already in use.");
|
|
|
|
|
}
|
|
|
|
|
if (msg.target == null) {
|
|
|
|
|
throw new AndroidRuntimeException("Message must have a target.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
if (mQuitting) {
|
|
|
|
|
RuntimeException e = new RuntimeException(
|
|
|
|
|
msg.target + " sending message to a Handler on a dead thread");
|
|
|
|
|
Log.w("MessageQueue", e.getMessage(), e);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msg.when = when;
|
|
|
|
|
Message p = mMessages;
|
|
|
|
|
boolean needWake;
|
|
|
|
|
if (p == null || when == 0 || when < p.when) {
|
|
|
|
|
// New head, wake up the event queue if blocked.
|
|
|
|
|
msg.next = p;
|
|
|
|
|
mMessages = msg;
|
|
|
|
|
needWake = mBlocked;
|
|
|
|
|
} else {
|
|
|
|
|
// Inserted within the middle of the queue. Usually we don't have to wake
|
|
|
|
|
// up the event queue unless there is a barrier at the head of the queue
|
|
|
|
|
// and the message is the earliest asynchronous message in the queue.
|
|
|
|
|
needWake = mBlocked && p.target == null && msg.isAsynchronous();
|
|
|
|
|
Message prev;
|
|
|
|
|
for (;;) {
|
|
|
|
|
prev = p;
|
|
|
|
|
p = p.next;
|
|
|
|
|
if (p == null || when < p.when) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (needWake && p.isAsynchronous()) {
|
|
|
|
|
needWake = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
msg.next = p; // invariant: p == prev.next
|
|
|
|
|
prev.next = msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We can assume mPtr != 0 because mQuitting is false.
|
|
|
|
|
if (needWake) {
|
|
|
|
|
nativeWake(mPtr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean hasMessages(Handler h, int what, Object object) {
|
|
|
|
|
if (h == null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
Message p = mMessages;
|
|
|
|
|
while (p != null) {
|
|
|
|
|
if (p.target == h && p.what == what && (object == null || p.obj == object)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
p = p.next;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean hasMessages(Handler h, Runnable r, Object object) {
|
|
|
|
|
if (h == null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
Message p = mMessages;
|
|
|
|
|
while (p != null) {
|
|
|
|
|
if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
p = p.next;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean isIdling() {
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
return isIdlingLocked();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean isIdlingLocked() {
|
|
|
|
|
// If the loop is quitting then it must not be idling.
|
|
|
|
|
// We can assume mPtr != 0 when mQuitting is false.
|
|
|
|
|
return !mQuitting && nativeIsIdling(mPtr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void removeMessages(Handler h, int what, Object object) {
|
|
|
|
|
if (h == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
Message p = mMessages;
|
|
|
|
|
|
|
|
|
|
// Remove all messages at front.
|
|
|
|
|
while (p != null && p.target == h && p.what == what && (object == null || p.obj == object)) {
|
|
|
|
|
Message n = p.next;
|
|
|
|
|
mMessages = n;
|
|
|
|
|
p.recycle();
|
|
|
|
|
p = n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove all messages after front.
|
|
|
|
|
while (p != null) {
|
|
|
|
|
Message n = p.next;
|
|
|
|
|
if (n != null) {
|
|
|
|
|
if (n.target == h && n.what == what && (object == null || n.obj == object)) {
|
|
|
|
|
Message nn = n.next;
|
|
|
|
|
n.recycle();
|
|
|
|
|
p.next = nn;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
p = n;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void removeMessages(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 || p.obj == object)) {
|
|
|
|
|
Message n = p.next;
|
|
|
|
|
mMessages = n;
|
|
|
|
|
p.recycle();
|
|
|
|
|
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 || n.obj == object)) {
|
|
|
|
|
Message nn = n.next;
|
|
|
|
|
n.recycle();
|
|
|
|
|
p.next = nn;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
p = n;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void removeCallbacksAndMessages(Handler h, Object object) {
|
|
|
|
|
if (h == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
Message p = mMessages;
|
|
|
|
|
|
|
|
|
|
// Remove all messages at front.
|
|
|
|
|
while (p != null && p.target == h && (object == null || p.obj == object)) {
|
|
|
|
|
Message n = p.next;
|
|
|
|
|
mMessages = n;
|
|
|
|
|
p.recycle();
|
|
|
|
|
p = n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove all messages after front.
|
|
|
|
|
while (p != null) {
|
|
|
|
|
Message n = p.next;
|
|
|
|
|
if (n != null) {
|
|
|
|
|
if (n.target == h && (object == null || n.obj == object)) {
|
|
|
|
|
Message nn = n.next;
|
|
|
|
|
n.recycle();
|
|
|
|
|
p.next = nn;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
p = n;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void removeAllMessagesLocked() {
|
|
|
|
|
Message p = mMessages;
|
|
|
|
|
while (p != null) {
|
|
|
|
|
Message n = p.next;
|
|
|
|
|
p.recycle();
|
|
|
|
|
p = n;
|
|
|
|
|
}
|
|
|
|
|
mMessages = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void removeAllFutureMessagesLocked() {
|
|
|
|
|
final long now = SystemClock.uptimeMillis();
|
|
|
|
|
Message p = mMessages;
|
|
|
|
|
if (p != null) {
|
|
|
|
|
if (p.when > now) {
|
|
|
|
|
removeAllMessagesLocked();
|
|
|
|
|
} else {
|
|
|
|
|
Message n;
|
|
|
|
|
for (;;) {
|
|
|
|
|
n = p.next;
|
|
|
|
|
if (n == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (n.when > now) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
p = n;
|
|
|
|
|
}
|
|
|
|
|
p.next = null;
|
|
|
|
|
do {
|
|
|
|
|
p = n;
|
|
|
|
|
n = p.next;
|
|
|
|
|
p.recycle();
|
|
|
|
|
} while (n != null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dump(Printer pw, String prefix) {
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
long now = SystemClock.uptimeMillis();
|
|
|
|
|
int n = 0;
|
|
|
|
|
for (Message msg = mMessages; msg != null; msg = msg.next) {
|
|
|
|
|
pw.println(prefix + "Message " + n + ": " + msg.toString(now));
|
|
|
|
|
n++;
|
|
|
|
|
}
|
|
|
|
|
pw.println(prefix + "(Total messages: " + n + ", idling=" + isIdlingLocked() + ", quitting=" + mQuitting + ")");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-02 23:06:56 +02:00
|
|
|
}
|