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