• 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     // True if the message queue can be quit.
34     private final boolean mQuitAllowed;
35 
36     @SuppressWarnings("unused")
37     private int mPtr; // used by native code
38 
39     Message mMessages;
40     private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
41     private IdleHandler[] mPendingIdleHandlers;
42     private boolean mQuiting;
43 
44     // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
45     private boolean mBlocked;
46 
47     // The next barrier token.
48     // Barriers are indicated by messages with a null target whose arg1 field carries the token.
49     private int mNextBarrierToken;
50 
nativeInit()51     private native void nativeInit();
nativeDestroy()52     private native void nativeDestroy();
nativePollOnce(int ptr, int timeoutMillis)53     private native void nativePollOnce(int ptr, int timeoutMillis);
nativeWake(int ptr)54     private native void nativeWake(int ptr);
55 
56     /**
57      * Callback interface for discovering when a thread is going to block
58      * waiting for more messages.
59      */
60     public static interface IdleHandler {
61         /**
62          * Called when the message queue has run out of messages and will now
63          * wait for more.  Return true to keep your idle handler active, false
64          * to have it removed.  This may be called if there are still messages
65          * pending in the queue, but they are all scheduled to be dispatched
66          * after the current time.
67          */
queueIdle()68         boolean queueIdle();
69     }
70 
71     /**
72      * Add a new {@link IdleHandler} to this message queue.  This may be
73      * removed automatically for you by returning false from
74      * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
75      * invoked, or explicitly removing it with {@link #removeIdleHandler}.
76      *
77      * <p>This method is safe to call from any thread.
78      *
79      * @param handler The IdleHandler to be added.
80      */
addIdleHandler(IdleHandler handler)81     public final void addIdleHandler(IdleHandler handler) {
82         if (handler == null) {
83             throw new NullPointerException("Can't add a null IdleHandler");
84         }
85         synchronized (this) {
86             mIdleHandlers.add(handler);
87         }
88     }
89 
90     /**
91      * Remove an {@link IdleHandler} from the queue that was previously added
92      * with {@link #addIdleHandler}.  If the given object is not currently
93      * in the idle list, nothing is done.
94      *
95      * @param handler The IdleHandler to be removed.
96      */
removeIdleHandler(IdleHandler handler)97     public final void removeIdleHandler(IdleHandler handler) {
98         synchronized (this) {
99             mIdleHandlers.remove(handler);
100         }
101     }
102 
MessageQueue(boolean quitAllowed)103     MessageQueue(boolean quitAllowed) {
104         mQuitAllowed = quitAllowed;
105         nativeInit();
106     }
107 
108     @Override
finalize()109     protected void finalize() throws Throwable {
110         try {
111             nativeDestroy();
112         } finally {
113             super.finalize();
114         }
115     }
116 
next()117     final Message next() {
118         int pendingIdleHandlerCount = -1; // -1 only during first iteration
119         int nextPollTimeoutMillis = 0;
120 
121         for (;;) {
122             if (nextPollTimeoutMillis != 0) {
123                 Binder.flushPendingCommands();
124             }
125             nativePollOnce(mPtr, nextPollTimeoutMillis);
126 
127             synchronized (this) {
128                 if (mQuiting) {
129                     return null;
130                 }
131 
132                 // Try to retrieve the next message.  Return if found.
133                 final long now = SystemClock.uptimeMillis();
134                 Message prevMsg = null;
135                 Message msg = mMessages;
136                 if (msg != null && msg.target == null) {
137                     // Stalled by a barrier.  Find the next asynchronous message in the queue.
138                     do {
139                         prevMsg = msg;
140                         msg = msg.next;
141                     } while (msg != null && !msg.isAsynchronous());
142                 }
143                 if (msg != null) {
144                     if (now < msg.when) {
145                         // Next message is not ready.  Set a timeout to wake up when it is ready.
146                         nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
147                     } else {
148                         // Got a message.
149                         mBlocked = false;
150                         if (prevMsg != null) {
151                             prevMsg.next = msg.next;
152                         } else {
153                             mMessages = msg.next;
154                         }
155                         msg.next = null;
156                         if (false) Log.v("MessageQueue", "Returning message: " + msg);
157                         msg.markInUse();
158                         return msg;
159                     }
160                 } else {
161                     // No more messages.
162                     nextPollTimeoutMillis = -1;
163                 }
164 
165                 // If first time idle, then get the number of idlers to run.
166                 // Idle handles only run if the queue is empty or if the first message
167                 // in the queue (possibly a barrier) is due to be handled in the future.
168                 if (pendingIdleHandlerCount < 0
169                         && (mMessages == null || now < mMessages.when)) {
170                     pendingIdleHandlerCount = mIdleHandlers.size();
171                 }
172                 if (pendingIdleHandlerCount <= 0) {
173                     // No idle handlers to run.  Loop and wait some more.
174                     mBlocked = true;
175                     continue;
176                 }
177 
178                 if (mPendingIdleHandlers == null) {
179                     mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
180                 }
181                 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
182             }
183 
184             // Run the idle handlers.
185             // We only ever reach this code block during the first iteration.
186             for (int i = 0; i < pendingIdleHandlerCount; i++) {
187                 final IdleHandler idler = mPendingIdleHandlers[i];
188                 mPendingIdleHandlers[i] = null; // release the reference to the handler
189 
190                 boolean keep = false;
191                 try {
192                     keep = idler.queueIdle();
193                 } catch (Throwable t) {
194                     Log.wtf("MessageQueue", "IdleHandler threw exception", t);
195                 }
196 
197                 if (!keep) {
198                     synchronized (this) {
199                         mIdleHandlers.remove(idler);
200                     }
201                 }
202             }
203 
204             // Reset the idle handler count to 0 so we do not run them again.
205             pendingIdleHandlerCount = 0;
206 
207             // While calling an idle handler, a new message could have been delivered
208             // so go back and look again for a pending message without waiting.
209             nextPollTimeoutMillis = 0;
210         }
211     }
212 
quit()213     final void quit() {
214         if (!mQuitAllowed) {
215             throw new RuntimeException("Main thread not allowed to quit.");
216         }
217 
218         synchronized (this) {
219             if (mQuiting) {
220                 return;
221             }
222             mQuiting = true;
223         }
224         nativeWake(mPtr);
225     }
226 
enqueueSyncBarrier(long when)227     final int enqueueSyncBarrier(long when) {
228         // Enqueue a new sync barrier token.
229         // We don't need to wake the queue because the purpose of a barrier is to stall it.
230         synchronized (this) {
231             final int token = mNextBarrierToken++;
232             final Message msg = Message.obtain();
233             msg.arg1 = token;
234 
235             Message prev = null;
236             Message p = mMessages;
237             if (when != 0) {
238                 while (p != null && p.when <= when) {
239                     prev = p;
240                     p = p.next;
241                 }
242             }
243             if (prev != null) { // invariant: p == prev.next
244                 msg.next = p;
245                 prev.next = msg;
246             } else {
247                 msg.next = p;
248                 mMessages = msg;
249             }
250             return token;
251         }
252     }
253 
removeSyncBarrier(int token)254     final void removeSyncBarrier(int token) {
255         // Remove a sync barrier token from the queue.
256         // If the queue is no longer stalled by a barrier then wake it.
257         final boolean needWake;
258         synchronized (this) {
259             Message prev = null;
260             Message p = mMessages;
261             while (p != null && (p.target != null || p.arg1 != token)) {
262                 prev = p;
263                 p = p.next;
264             }
265             if (p == null) {
266                 throw new IllegalStateException("The specified message queue synchronization "
267                         + " barrier token has not been posted or has already been removed.");
268             }
269             if (prev != null) {
270                 prev.next = p.next;
271                 needWake = false;
272             } else {
273                 mMessages = p.next;
274                 needWake = mMessages == null || mMessages.target != null;
275             }
276             p.recycle();
277         }
278         if (needWake) {
279             nativeWake(mPtr);
280         }
281     }
282 
enqueueMessage(Message msg, long when)283     final boolean enqueueMessage(Message msg, long when) {
284         if (msg.isInUse()) {
285             throw new AndroidRuntimeException(msg + " This message is already in use.");
286         }
287         if (msg.target == null) {
288             throw new AndroidRuntimeException("Message must have a target.");
289         }
290 
291         boolean needWake;
292         synchronized (this) {
293             if (mQuiting) {
294                 RuntimeException e = new RuntimeException(
295                         msg.target + " sending message to a Handler on a dead thread");
296                 Log.w("MessageQueue", e.getMessage(), e);
297                 return false;
298             }
299 
300             msg.when = when;
301             Message p = mMessages;
302             if (p == null || when == 0 || when < p.when) {
303                 // New head, wake up the event queue if blocked.
304                 msg.next = p;
305                 mMessages = msg;
306                 needWake = mBlocked;
307             } else {
308                 // Inserted within the middle of the queue.  Usually we don't have to wake
309                 // up the event queue unless there is a barrier at the head of the queue
310                 // and the message is the earliest asynchronous message in the queue.
311                 needWake = mBlocked && p.target == null && msg.isAsynchronous();
312                 Message prev;
313                 for (;;) {
314                     prev = p;
315                     p = p.next;
316                     if (p == null || when < p.when) {
317                         break;
318                     }
319                     if (needWake && p.isAsynchronous()) {
320                         needWake = false;
321                     }
322                 }
323                 msg.next = p; // invariant: p == prev.next
324                 prev.next = msg;
325             }
326         }
327         if (needWake) {
328             nativeWake(mPtr);
329         }
330         return true;
331     }
332 
hasMessages(Handler h, int what, Object object)333     final boolean hasMessages(Handler h, int what, Object object) {
334         if (h == null) {
335             return false;
336         }
337 
338         synchronized (this) {
339             Message p = mMessages;
340             while (p != null) {
341                 if (p.target == h && p.what == what && (object == null || p.obj == object)) {
342                     return true;
343                 }
344                 p = p.next;
345             }
346             return false;
347         }
348     }
349 
hasMessages(Handler h, Runnable r, Object object)350     final boolean hasMessages(Handler h, Runnable r, Object object) {
351         if (h == null) {
352             return false;
353         }
354 
355         synchronized (this) {
356             Message p = mMessages;
357             while (p != null) {
358                 if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
359                     return true;
360                 }
361                 p = p.next;
362             }
363             return false;
364         }
365     }
366 
removeMessages(Handler h, int what, Object object)367     final void removeMessages(Handler h, int what, Object object) {
368         if (h == null) {
369             return;
370         }
371 
372         synchronized (this) {
373             Message p = mMessages;
374 
375             // Remove all messages at front.
376             while (p != null && p.target == h && p.what == what
377                    && (object == null || p.obj == object)) {
378                 Message n = p.next;
379                 mMessages = n;
380                 p.recycle();
381                 p = n;
382             }
383 
384             // Remove all messages after front.
385             while (p != null) {
386                 Message n = p.next;
387                 if (n != null) {
388                     if (n.target == h && n.what == what
389                         && (object == null || n.obj == object)) {
390                         Message nn = n.next;
391                         n.recycle();
392                         p.next = nn;
393                         continue;
394                     }
395                 }
396                 p = n;
397             }
398         }
399     }
400 
removeMessages(Handler h, Runnable r, Object object)401     final void removeMessages(Handler h, Runnable r, Object object) {
402         if (h == null || r == null) {
403             return;
404         }
405 
406         synchronized (this) {
407             Message p = mMessages;
408 
409             // Remove all messages at front.
410             while (p != null && p.target == h && p.callback == r
411                    && (object == null || p.obj == object)) {
412                 Message n = p.next;
413                 mMessages = n;
414                 p.recycle();
415                 p = n;
416             }
417 
418             // Remove all messages after front.
419             while (p != null) {
420                 Message n = p.next;
421                 if (n != null) {
422                     if (n.target == h && n.callback == r
423                         && (object == null || n.obj == object)) {
424                         Message nn = n.next;
425                         n.recycle();
426                         p.next = nn;
427                         continue;
428                     }
429                 }
430                 p = n;
431             }
432         }
433     }
434 
removeCallbacksAndMessages(Handler h, Object object)435     final void removeCallbacksAndMessages(Handler h, Object object) {
436         if (h == null) {
437             return;
438         }
439 
440         synchronized (this) {
441             Message p = mMessages;
442 
443             // Remove all messages at front.
444             while (p != null && p.target == h
445                     && (object == null || p.obj == object)) {
446                 Message n = p.next;
447                 mMessages = n;
448                 p.recycle();
449                 p = n;
450             }
451 
452             // Remove all messages after front.
453             while (p != null) {
454                 Message n = p.next;
455                 if (n != null) {
456                     if (n.target == h && (object == null || n.obj == object)) {
457                         Message nn = n.next;
458                         n.recycle();
459                         p.next = nn;
460                         continue;
461                     }
462                 }
463                 p = n;
464             }
465         }
466     }
467 }
468