MessageQueue.java revision ddb2bfeb6e5a383306159672fe81333bf8520776
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                    return msg;
124                }
125
126                if (tryIdle && mIdleHandlers.size() > 0) {
127                    idlers = mIdleHandlers.toArray();
128                }
129            }
130
131            // There was no message so we are going to wait...  but first,
132            // if there are any idle handlers let them know.
133            boolean didIdle = false;
134            if (idlers != null) {
135                for (Object idler : idlers) {
136                    boolean keep = false;
137                    try {
138                        didIdle = true;
139                        keep = ((IdleHandler)idler).queueIdle();
140                    } catch (Throwable t) {
141                        Log.wtf("MessageQueue", "IdleHandler threw exception", t);
142                    }
143
144                    if (!keep) {
145                        synchronized (this) {
146                            mIdleHandlers.remove(idler);
147                        }
148                    }
149                }
150            }
151
152            // While calling an idle handler, a new message could have been
153            // delivered...  so go back and look again for a pending message.
154            if (didIdle) {
155                tryIdle = false;
156                continue;
157            }
158
159            synchronized (this) {
160                // No messages, nobody to tell about it...  time to wait!
161                if (mMessages != null) {
162                    long longTimeToNextEventMillis = mMessages.when - now;
163
164                    if (longTimeToNextEventMillis > 0) {
165                        Binder.flushPendingCommands();
166                        timeToNextEventMillis = (int) Math.min(longTimeToNextEventMillis,
167                                Integer.MAX_VALUE);
168                    } else {
169                        timeToNextEventMillis = 0;
170                    }
171                } else {
172                    Binder.flushPendingCommands();
173                    timeToNextEventMillis = -1;
174                }
175            }
176            // loop to the while(true) and do the appropriate nativeWait(when)
177        }
178    }
179
180    final Message pullNextLocked(long now) {
181        Message msg = mMessages;
182        if (msg != null) {
183            if (now >= msg.when) {
184                mMessages = msg.next;
185                if (Config.LOGV) Log.v(
186                    "MessageQueue", "Returning message: " + msg);
187                return msg;
188            }
189        }
190
191        return null;
192    }
193
194    final boolean enqueueMessage(Message msg, long when) {
195        if (msg.when != 0) {
196            throw new AndroidRuntimeException(msg
197                    + " This message is already in use.");
198        }
199        if (msg.target == null && !mQuitAllowed) {
200            throw new RuntimeException("Main thread not allowed to quit");
201        }
202        synchronized (this) {
203            if (mQuiting) {
204                RuntimeException e = new RuntimeException(
205                    msg.target + " sending message to a Handler on a dead thread");
206                Log.w("MessageQueue", e.getMessage(), e);
207                return false;
208            } else if (msg.target == null) {
209                mQuiting = true;
210            }
211
212            msg.when = when;
213            //Log.d("MessageQueue", "Enqueing: " + msg);
214            Message p = mMessages;
215            if (p == null || when == 0 || when < p.when) {
216                msg.next = p;
217                mMessages = msg;
218            } else {
219                Message prev = null;
220                while (p != null && p.when <= when) {
221                    prev = p;
222                    p = p.next;
223                }
224                msg.next = prev.next;
225                prev.next = msg;
226            }
227            nativeWake();
228        }
229        return true;
230    }
231
232    final boolean removeMessages(Handler h, int what, Object object,
233            boolean doRemove) {
234        synchronized (this) {
235            Message p = mMessages;
236            boolean found = false;
237
238            // Remove all messages at front.
239            while (p != null && p.target == h && p.what == what
240                   && (object == null || p.obj == object)) {
241                if (!doRemove) return true;
242                found = true;
243                Message n = p.next;
244                mMessages = n;
245                p.recycle();
246                p = n;
247            }
248
249            // Remove all messages after front.
250            while (p != null) {
251                Message n = p.next;
252                if (n != null) {
253                    if (n.target == h && n.what == what
254                        && (object == null || n.obj == object)) {
255                        if (!doRemove) return true;
256                        found = true;
257                        Message nn = n.next;
258                        n.recycle();
259                        p.next = nn;
260                        continue;
261                    }
262                }
263                p = n;
264            }
265
266            return found;
267        }
268    }
269
270    final void removeMessages(Handler h, Runnable r, Object object) {
271        if (r == null) {
272            return;
273        }
274
275        synchronized (this) {
276            Message p = mMessages;
277
278            // Remove all messages at front.
279            while (p != null && p.target == h && p.callback == r
280                   && (object == null || p.obj == object)) {
281                Message n = p.next;
282                mMessages = n;
283                p.recycle();
284                p = n;
285            }
286
287            // Remove all messages after front.
288            while (p != null) {
289                Message n = p.next;
290                if (n != null) {
291                    if (n.target == h && n.callback == r
292                        && (object == null || n.obj == object)) {
293                        Message nn = n.next;
294                        n.recycle();
295                        p.next = nn;
296                        continue;
297                    }
298                }
299                p = n;
300            }
301        }
302    }
303
304    final void removeCallbacksAndMessages(Handler h, Object object) {
305        synchronized (this) {
306            Message p = mMessages;
307
308            // Remove all messages at front.
309            while (p != null && p.target == h
310                    && (object == null || p.obj == object)) {
311                Message n = p.next;
312                mMessages = n;
313                p.recycle();
314                p = n;
315            }
316
317            // Remove all messages after front.
318            while (p != null) {
319                Message n = p.next;
320                if (n != null) {
321                    if (n.target == h && (object == null || n.obj == object)) {
322                        Message nn = n.next;
323                        n.recycle();
324                        p.next = nn;
325                        continue;
326                    }
327                }
328                p = n;
329            }
330        }
331    }
332
333    /*
334    private void dumpQueue_l()
335    {
336        Message p = mMessages;
337        System.out.println(this + "  queue is:");
338        while (p != null) {
339            System.out.println("            " + p);
340            p = p.next;
341        }
342    }
343    */
344}
345