MessageQueue.java revision 6083d81ce4d67ec632962270fda64ebb9db0d5b1
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.os;
18
19import android.util.AndroidRuntimeException;
20import android.util.Config;
21import android.util.Log;
22
23import java.util.ArrayList;
24
25/**
26 * Low-level class holding the list of messages to be dispatched by a
27 * {@link Looper}.  Messages are not added directly to a MessageQueue,
28 * but rather through {@link Handler} objects associated with the Looper.
29 *
30 * <p>You can retrieve the MessageQueue for the current thread with
31 * {@link Looper#myQueue() Looper.myQueue()}.
32 */
33public class MessageQueue {
34    Message mMessages;
35    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
36    private boolean mQuiting = false;
37    boolean mQuitAllowed = true;
38
39    @SuppressWarnings("unused")
40    private int mPtr; // used by native code
41
42    private native void nativeInit();
43    private native void nativeDestroy();
44    private native boolean nativePollOnce(int timeoutMillis);
45    private native void nativeWake();
46
47    /**
48     * Callback interface for discovering when a thread is going to block
49     * waiting for more messages.
50     */
51    public static interface IdleHandler {
52        /**
53         * Called when the message queue has run out of messages and will now
54         * wait for more.  Return true to keep your idle handler active, false
55         * to have it removed.  This may be called if there are still messages
56         * pending in the queue, but they are all scheduled to be dispatched
57         * after the current time.
58         */
59        boolean queueIdle();
60    }
61
62    /**
63     * Add a new {@link IdleHandler} to this message queue.  This may be
64     * removed automatically for you by returning false from
65     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
66     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
67     *
68     * <p>This method is safe to call from any thread.
69     *
70     * @param handler The IdleHandler to be added.
71     */
72    public final void addIdleHandler(IdleHandler handler) {
73        if (handler == null) {
74            throw new NullPointerException("Can't add a null IdleHandler");
75        }
76        synchronized (this) {
77            mIdleHandlers.add(handler);
78        }
79    }
80
81    /**
82     * Remove an {@link IdleHandler} from the queue that was previously added
83     * with {@link #addIdleHandler}.  If the given object is not currently
84     * in the idle list, nothing is done.
85     *
86     * @param handler The IdleHandler to be removed.
87     */
88    public final void removeIdleHandler(IdleHandler handler) {
89        synchronized (this) {
90            mIdleHandlers.remove(handler);
91        }
92    }
93
94    MessageQueue() {
95        nativeInit();
96    }
97
98    @Override
99    protected void finalize() throws Throwable {
100        try {
101            nativeDestroy();
102        } finally {
103            super.finalize();
104        }
105    }
106
107    final Message next() {
108        boolean tryIdle = true;
109        // when we start out, we'll just touch the input pipes and then go from there
110        int timeToNextEventMillis = 0;
111
112        while (true) {
113            long now;
114            Object[] idlers = null;
115
116            boolean dispatched = nativePollOnce(timeToNextEventMillis);
117
118            // Try to retrieve the next message, returning if found.
119            synchronized (this) {
120                now = SystemClock.uptimeMillis();
121                Message msg = pullNextLocked(now);
122                if (msg != null) {
123                    msg.markInUse();
124                    return msg;
125                }
126
127                if (tryIdle && mIdleHandlers.size() > 0) {
128                    idlers = mIdleHandlers.toArray();
129                }
130            }
131
132            // There was no message so we are going to wait...  but first,
133            // if there are any idle handlers let them know.
134            boolean didIdle = false;
135            if (idlers != null) {
136                for (Object idler : idlers) {
137                    boolean keep = false;
138                    try {
139                        didIdle = true;
140                        keep = ((IdleHandler)idler).queueIdle();
141                    } catch (Throwable t) {
142                        Log.wtf("MessageQueue", "IdleHandler threw exception", t);
143                    }
144
145                    if (!keep) {
146                        synchronized (this) {
147                            mIdleHandlers.remove(idler);
148                        }
149                    }
150                }
151            }
152
153            // While calling an idle handler, a new message could have been
154            // delivered...  so go back and look again for a pending message.
155            if (didIdle) {
156                tryIdle = false;
157                continue;
158            }
159
160            synchronized (this) {
161                // No messages, nobody to tell about it...  time to wait!
162                if (mMessages != null) {
163                    long longTimeToNextEventMillis = mMessages.when - now;
164
165                    if (longTimeToNextEventMillis > 0) {
166                        Binder.flushPendingCommands();
167                        timeToNextEventMillis = (int) Math.min(longTimeToNextEventMillis,
168                                Integer.MAX_VALUE);
169                    } else {
170                        timeToNextEventMillis = 0;
171                    }
172                } else {
173                    Binder.flushPendingCommands();
174                    timeToNextEventMillis = -1;
175                }
176            }
177            // loop to the while(true) and do the appropriate nativeWait(when)
178        }
179    }
180
181    final Message pullNextLocked(long now) {
182        Message msg = mMessages;
183        if (msg != null) {
184            if (now >= msg.when) {
185                mMessages = msg.next;
186                if (Config.LOGV) Log.v(
187                    "MessageQueue", "Returning message: " + msg);
188                return msg;
189            }
190        }
191
192        return null;
193    }
194
195    final boolean enqueueMessage(Message msg, long when) {
196        if (msg.isInUse()) {
197            throw new AndroidRuntimeException(msg
198                    + " This message is already in use.");
199        }
200        if (msg.target == null && !mQuitAllowed) {
201            throw new RuntimeException("Main thread not allowed to quit");
202        }
203        synchronized (this) {
204            if (mQuiting) {
205                RuntimeException e = new RuntimeException(
206                    msg.target + " sending message to a Handler on a dead thread");
207                Log.w("MessageQueue", e.getMessage(), e);
208                return false;
209            } else if (msg.target == null) {
210                mQuiting = true;
211            }
212
213            msg.when = when;
214            //Log.d("MessageQueue", "Enqueing: " + msg);
215            Message p = mMessages;
216            if (p == null || when == 0 || when < p.when) {
217                msg.next = p;
218                mMessages = msg;
219            } else {
220                Message prev = null;
221                while (p != null && p.when <= when) {
222                    prev = p;
223                    p = p.next;
224                }
225                msg.next = prev.next;
226                prev.next = msg;
227            }
228            nativeWake();
229        }
230        return true;
231    }
232
233    final boolean removeMessages(Handler h, int what, Object object,
234            boolean doRemove) {
235        synchronized (this) {
236            Message p = mMessages;
237            boolean found = false;
238
239            // Remove all messages at front.
240            while (p != null && p.target == h && p.what == what
241                   && (object == null || p.obj == object)) {
242                if (!doRemove) return true;
243                found = true;
244                Message n = p.next;
245                mMessages = n;
246                p.recycle();
247                p = n;
248            }
249
250            // Remove all messages after front.
251            while (p != null) {
252                Message n = p.next;
253                if (n != null) {
254                    if (n.target == h && n.what == what
255                        && (object == null || n.obj == object)) {
256                        if (!doRemove) return true;
257                        found = true;
258                        Message nn = n.next;
259                        n.recycle();
260                        p.next = nn;
261                        continue;
262                    }
263                }
264                p = n;
265            }
266
267            return found;
268        }
269    }
270
271    final void removeMessages(Handler h, Runnable r, Object object) {
272        if (r == null) {
273            return;
274        }
275
276        synchronized (this) {
277            Message p = mMessages;
278
279            // Remove all messages at front.
280            while (p != null && p.target == h && p.callback == r
281                   && (object == null || p.obj == object)) {
282                Message n = p.next;
283                mMessages = n;
284                p.recycle();
285                p = n;
286            }
287
288            // Remove all messages after front.
289            while (p != null) {
290                Message n = p.next;
291                if (n != null) {
292                    if (n.target == h && n.callback == r
293                        && (object == null || n.obj == object)) {
294                        Message nn = n.next;
295                        n.recycle();
296                        p.next = nn;
297                        continue;
298                    }
299                }
300                p = n;
301            }
302        }
303    }
304
305    final void removeCallbacksAndMessages(Handler h, Object object) {
306        synchronized (this) {
307            Message p = mMessages;
308
309            // Remove all messages at front.
310            while (p != null && p.target == h
311                    && (object == null || p.obj == object)) {
312                Message n = p.next;
313                mMessages = n;
314                p.recycle();
315                p = n;
316            }
317
318            // Remove all messages after front.
319            while (p != null) {
320                Message n = p.next;
321                if (n != null) {
322                    if (n.target == h && (object == null || n.obj == object)) {
323                        Message nn = n.next;
324                        n.recycle();
325                        p.next = nn;
326                        continue;
327                    }
328                }
329                p = n;
330            }
331        }
332    }
333
334    /*
335    private void dumpQueue_l()
336    {
337        Message p = mMessages;
338        System.out.println(this + "  queue is:");
339        while (p != null) {
340            System.out.println("            " + p);
341            p = p.next;
342        }
343    }
344    */
345}
346