/* * 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.Config; import android.util.Log; 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. * *
You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
public class MessageQueue {
Message mMessages;
private final ArrayList This method is safe to call from any thread.
*
* @param handler The IdleHandler to be added.
*/
public final 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 final void removeIdleHandler(IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
MessageQueue() {
nativeInit();
}
@Override
protected void finalize() throws Throwable {
try {
nativeDestroy();
} finally {
super.finalize();
}
}
final Message next() {
boolean tryIdle = true;
// when we start out, we'll just touch the input pipes and then go from there
int timeToNextEventMillis = 0;
while (true) {
long now;
Object[] idlers = null;
boolean dispatched = nativePollOnce(timeToNextEventMillis);
// Try to retrieve the next message, returning if found.
synchronized (this) {
now = SystemClock.uptimeMillis();
Message msg = pullNextLocked(now);
if (msg != null) {
return msg;
}
if (tryIdle && mIdleHandlers.size() > 0) {
idlers = mIdleHandlers.toArray();
}
}
// There was no message so we are going to wait... but first,
// if there are any idle handlers let them know.
boolean didIdle = false;
if (idlers != null) {
for (Object idler : idlers) {
boolean keep = false;
try {
didIdle = true;
keep = ((IdleHandler)idler).queueIdle();
} catch (Throwable t) {
Log.wtf("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
}
// While calling an idle handler, a new message could have been
// delivered... so go back and look again for a pending message.
if (didIdle) {
tryIdle = false;
continue;
}
synchronized (this) {
// No messages, nobody to tell about it... time to wait!
if (mMessages != null) {
long longTimeToNextEventMillis = mMessages.when - now;
if (longTimeToNextEventMillis > 0) {
Binder.flushPendingCommands();
timeToNextEventMillis = (int) Math.min(longTimeToNextEventMillis,
Integer.MAX_VALUE);
} else {
timeToNextEventMillis = 0;
}
} else {
Binder.flushPendingCommands();
timeToNextEventMillis = -1;
}
}
// loop to the while(true) and do the appropriate nativeWait(when)
}
}
final Message pullNextLocked(long now) {
Message msg = mMessages;
if (msg != null) {
if (now >= msg.when) {
mMessages = msg.next;
if (Config.LOGV) Log.v(
"MessageQueue", "Returning message: " + msg);
return msg;
}
}
return null;
}
final boolean enqueueMessage(Message msg, long when) {
if (msg.when != 0) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
}
nativeWake();
}
return true;
}
final boolean removeMessages(Handler h, int what, Object object,
boolean doRemove) {
synchronized (this) {
Message p = mMessages;
boolean found = false;
// Remove all messages at front.
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
if (!doRemove) return true;
found = true;
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)) {
if (!doRemove) return true;
found = true;
Message nn = n.next;
n.recycle();
p.next = nn;
continue;
}
}
p = n;
}
return found;
}
}
final void removeMessages(Handler h, Runnable r, Object object) {
if (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;
}
}
}
final void removeCallbacksAndMessages(Handler h, Object object) {
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 dumpQueue_l()
{
Message p = mMessages;
System.out.println(this + " queue is:");
while (p != null) {
System.out.println(" " + p);
p = p.next;
}
}
*/
}