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