MessageQueue.java revision 3d4e7efe37a4b0dfc5807444e8c3b98a28953377
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.Log; 20import android.util.Printer; 21 22import 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 */ 32public final class MessageQueue { 33 // True if the message queue can be quit. 34 private final boolean mQuitAllowed; 35 36 @SuppressWarnings("unused") 37 private long 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 mQuitting; 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 51 private native static long nativeInit(); 52 private native static void nativeDestroy(long ptr); 53 private native static void nativePollOnce(long ptr, int timeoutMillis); 54 private native static void nativeWake(long ptr); 55 private native static boolean nativeIsPolling(long ptr); 56 57 MessageQueue(boolean quitAllowed) { 58 mQuitAllowed = quitAllowed; 59 mPtr = nativeInit(); 60 } 61 62 @Override 63 protected void finalize() throws Throwable { 64 try { 65 dispose(); 66 } finally { 67 super.finalize(); 68 } 69 } 70 71 // Disposes of the underlying message queue. 72 // Must only be called on the looper thread or the finalizer. 73 private void dispose() { 74 if (mPtr != 0) { 75 nativeDestroy(mPtr); 76 mPtr = 0; 77 } 78 } 79 80 /** 81 * Returns true if the looper has no pending messages which are due to be processed. 82 * 83 * <p>This method is safe to call from any thread. 84 * 85 * @return True if the looper is idle. 86 */ 87 public boolean isIdle() { 88 synchronized (this) { 89 final long now = SystemClock.uptimeMillis(); 90 return mMessages == null || now < mMessages.when; 91 } 92 } 93 94 /** 95 * Add a new {@link IdleHandler} to this message queue. This may be 96 * removed automatically for you by returning false from 97 * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is 98 * invoked, or explicitly removing it with {@link #removeIdleHandler}. 99 * 100 * <p>This method is safe to call from any thread. 101 * 102 * @param handler The IdleHandler to be added. 103 */ 104 public void addIdleHandler(IdleHandler handler) { 105 if (handler == null) { 106 throw new NullPointerException("Can't add a null IdleHandler"); 107 } 108 synchronized (this) { 109 mIdleHandlers.add(handler); 110 } 111 } 112 113 /** 114 * Remove an {@link IdleHandler} from the queue that was previously added 115 * with {@link #addIdleHandler}. If the given object is not currently 116 * in the idle list, nothing is done. 117 * 118 * <p>This method is safe to call from any thread. 119 * 120 * @param handler The IdleHandler to be removed. 121 */ 122 public void removeIdleHandler(IdleHandler handler) { 123 synchronized (this) { 124 mIdleHandlers.remove(handler); 125 } 126 } 127 128 /** 129 * Returns whether this looper's thread is currently polling for more work to do. 130 * This is a good signal that the loop is still alive rather than being stuck 131 * handling a callback. Note that this method is intrinsically racy, since the 132 * state of the loop can change before you get the result back. 133 * 134 * <p>This method is safe to call from any thread. 135 * 136 * @return True if the looper is currently polling for events. 137 * @hide 138 */ 139 public boolean isPolling() { 140 synchronized (this) { 141 return isPollingLocked(); 142 } 143 } 144 145 private boolean isPollingLocked() { 146 // If the loop is quitting then it must not be idling. 147 // We can assume mPtr != 0 when mQuitting is false. 148 return !mQuitting && nativeIsPolling(mPtr); 149 } 150 151 Message next() { 152 // Return here if the message loop has already quit and been disposed. 153 // This can happen if the application tries to restart a looper after quit 154 // which is not supported. 155 final long ptr = mPtr; 156 if (ptr == 0) { 157 return null; 158 } 159 160 int pendingIdleHandlerCount = -1; // -1 only during first iteration 161 int nextPollTimeoutMillis = 0; 162 for (;;) { 163 if (nextPollTimeoutMillis != 0) { 164 Binder.flushPendingCommands(); 165 } 166 167 nativePollOnce(ptr, nextPollTimeoutMillis); 168 169 synchronized (this) { 170 // Try to retrieve the next message. Return if found. 171 final long now = SystemClock.uptimeMillis(); 172 Message prevMsg = null; 173 Message msg = mMessages; 174 if (msg != null && msg.target == null) { 175 // Stalled by a barrier. Find the next asynchronous message in the queue. 176 do { 177 prevMsg = msg; 178 msg = msg.next; 179 } while (msg != null && !msg.isAsynchronous()); 180 } 181 if (msg != null) { 182 if (now < msg.when) { 183 // Next message is not ready. Set a timeout to wake up when it is ready. 184 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); 185 } else { 186 // Got a message. 187 mBlocked = false; 188 if (prevMsg != null) { 189 prevMsg.next = msg.next; 190 } else { 191 mMessages = msg.next; 192 } 193 msg.next = null; 194 if (false) Log.v("MessageQueue", "Returning message: " + msg); 195 return msg; 196 } 197 } else { 198 // No more messages. 199 nextPollTimeoutMillis = -1; 200 } 201 202 // Process the quit message now that all pending messages have been handled. 203 if (mQuitting) { 204 dispose(); 205 return null; 206 } 207 208 // If first time idle, then get the number of idlers to run. 209 // Idle handles only run if the queue is empty or if the first message 210 // in the queue (possibly a barrier) is due to be handled in the future. 211 if (pendingIdleHandlerCount < 0 212 && (mMessages == null || now < mMessages.when)) { 213 pendingIdleHandlerCount = mIdleHandlers.size(); 214 } 215 if (pendingIdleHandlerCount <= 0) { 216 // No idle handlers to run. Loop and wait some more. 217 mBlocked = true; 218 continue; 219 } 220 221 if (mPendingIdleHandlers == null) { 222 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; 223 } 224 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); 225 } 226 227 // Run the idle handlers. 228 // We only ever reach this code block during the first iteration. 229 for (int i = 0; i < pendingIdleHandlerCount; i++) { 230 final IdleHandler idler = mPendingIdleHandlers[i]; 231 mPendingIdleHandlers[i] = null; // release the reference to the handler 232 233 boolean keep = false; 234 try { 235 keep = idler.queueIdle(); 236 } catch (Throwable t) { 237 Log.wtf("MessageQueue", "IdleHandler threw exception", t); 238 } 239 240 if (!keep) { 241 synchronized (this) { 242 mIdleHandlers.remove(idler); 243 } 244 } 245 } 246 247 // Reset the idle handler count to 0 so we do not run them again. 248 pendingIdleHandlerCount = 0; 249 250 // While calling an idle handler, a new message could have been delivered 251 // so go back and look again for a pending message without waiting. 252 nextPollTimeoutMillis = 0; 253 } 254 } 255 256 void quit(boolean safe) { 257 if (!mQuitAllowed) { 258 throw new IllegalStateException("Main thread not allowed to quit."); 259 } 260 261 synchronized (this) { 262 if (mQuitting) { 263 return; 264 } 265 mQuitting = true; 266 267 if (safe) { 268 removeAllFutureMessagesLocked(); 269 } else { 270 removeAllMessagesLocked(); 271 } 272 273 // We can assume mPtr != 0 because mQuitting was previously false. 274 nativeWake(mPtr); 275 } 276 } 277 278 /** 279 * Posts a synchronization barrier to the Looper's message queue. 280 * 281 * Message processing occurs as usual until the message queue encounters the 282 * synchronization barrier that has been posted. When the barrier is encountered, 283 * later synchronous messages in the queue are stalled (prevented from being executed) 284 * until the barrier is released by calling {@link #removeSyncBarrier} and specifying 285 * the token that identifies the synchronization barrier. 286 * 287 * This method is used to immediately postpone execution of all subsequently posted 288 * synchronous messages until a condition is met that releases the barrier. 289 * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier 290 * and continue to be processed as usual. 291 * 292 * This call must be always matched by a call to {@link #removeSyncBarrier} with 293 * the same token to ensure that the message queue resumes normal operation. 294 * Otherwise the application will probably hang! 295 * 296 * @return A token that uniquely identifies the barrier. This token must be 297 * passed to {@link #removeSyncBarrier} to release the barrier. 298 * 299 * @hide 300 */ 301 public int postSyncBarrier() { 302 return postSyncBarrier(SystemClock.uptimeMillis()); 303 } 304 305 private int postSyncBarrier(long when) { 306 // Enqueue a new sync barrier token. 307 // We don't need to wake the queue because the purpose of a barrier is to stall it. 308 synchronized (this) { 309 final int token = mNextBarrierToken++; 310 final Message msg = Message.obtain(); 311 msg.markInUse(); 312 msg.when = when; 313 msg.arg1 = token; 314 315 Message prev = null; 316 Message p = mMessages; 317 if (when != 0) { 318 while (p != null && p.when <= when) { 319 prev = p; 320 p = p.next; 321 } 322 } 323 if (prev != null) { // invariant: p == prev.next 324 msg.next = p; 325 prev.next = msg; 326 } else { 327 msg.next = p; 328 mMessages = msg; 329 } 330 return token; 331 } 332 } 333 334 /** 335 * Removes a synchronization barrier. 336 * 337 * @param token The synchronization barrier token that was returned by 338 * {@link #postSyncBarrier}. 339 * 340 * @throws IllegalStateException if the barrier was not found. 341 * 342 * @hide 343 */ 344 public void removeSyncBarrier(int token) { 345 // Remove a sync barrier token from the queue. 346 // If the queue is no longer stalled by a barrier then wake it. 347 synchronized (this) { 348 Message prev = null; 349 Message p = mMessages; 350 while (p != null && (p.target != null || p.arg1 != token)) { 351 prev = p; 352 p = p.next; 353 } 354 if (p == null) { 355 throw new IllegalStateException("The specified message queue synchronization " 356 + " barrier token has not been posted or has already been removed."); 357 } 358 final boolean needWake; 359 if (prev != null) { 360 prev.next = p.next; 361 needWake = false; 362 } else { 363 mMessages = p.next; 364 needWake = mMessages == null || mMessages.target != null; 365 } 366 p.recycleUnchecked(); 367 368 // If the loop is quitting then it is already awake. 369 // We can assume mPtr != 0 when mQuitting is false. 370 if (needWake && !mQuitting) { 371 nativeWake(mPtr); 372 } 373 } 374 } 375 376 boolean enqueueMessage(Message msg, long when) { 377 if (msg.target == null) { 378 throw new IllegalArgumentException("Message must have a target."); 379 } 380 if (msg.isInUse()) { 381 throw new IllegalStateException(msg + " This message is already in use."); 382 } 383 384 synchronized (this) { 385 if (mQuitting) { 386 IllegalStateException e = new IllegalStateException( 387 msg.target + " sending message to a Handler on a dead thread"); 388 Log.w("MessageQueue", e.getMessage(), e); 389 msg.recycle(); 390 return false; 391 } 392 393 msg.markInUse(); 394 msg.when = when; 395 Message p = mMessages; 396 boolean needWake; 397 if (p == null || when == 0 || when < p.when) { 398 // New head, wake up the event queue if blocked. 399 msg.next = p; 400 mMessages = msg; 401 needWake = mBlocked; 402 } else { 403 // Inserted within the middle of the queue. Usually we don't have to wake 404 // up the event queue unless there is a barrier at the head of the queue 405 // and the message is the earliest asynchronous message in the queue. 406 needWake = mBlocked && p.target == null && msg.isAsynchronous(); 407 Message prev; 408 for (;;) { 409 prev = p; 410 p = p.next; 411 if (p == null || when < p.when) { 412 break; 413 } 414 if (needWake && p.isAsynchronous()) { 415 needWake = false; 416 } 417 } 418 msg.next = p; // invariant: p == prev.next 419 prev.next = msg; 420 } 421 422 // We can assume mPtr != 0 because mQuitting is false. 423 if (needWake) { 424 nativeWake(mPtr); 425 } 426 } 427 return true; 428 } 429 430 boolean hasMessages(Handler h, int what, Object object) { 431 if (h == null) { 432 return false; 433 } 434 435 synchronized (this) { 436 Message p = mMessages; 437 while (p != null) { 438 if (p.target == h && p.what == what && (object == null || p.obj == object)) { 439 return true; 440 } 441 p = p.next; 442 } 443 return false; 444 } 445 } 446 447 boolean hasMessages(Handler h, Runnable r, Object object) { 448 if (h == null) { 449 return false; 450 } 451 452 synchronized (this) { 453 Message p = mMessages; 454 while (p != null) { 455 if (p.target == h && p.callback == r && (object == null || p.obj == object)) { 456 return true; 457 } 458 p = p.next; 459 } 460 return false; 461 } 462 } 463 464 void removeMessages(Handler h, int what, Object object) { 465 if (h == null) { 466 return; 467 } 468 469 synchronized (this) { 470 Message p = mMessages; 471 472 // Remove all messages at front. 473 while (p != null && p.target == h && p.what == what 474 && (object == null || p.obj == object)) { 475 Message n = p.next; 476 mMessages = n; 477 p.recycleUnchecked(); 478 p = n; 479 } 480 481 // Remove all messages after front. 482 while (p != null) { 483 Message n = p.next; 484 if (n != null) { 485 if (n.target == h && n.what == what 486 && (object == null || n.obj == object)) { 487 Message nn = n.next; 488 n.recycleUnchecked(); 489 p.next = nn; 490 continue; 491 } 492 } 493 p = n; 494 } 495 } 496 } 497 498 void removeMessages(Handler h, Runnable r, Object object) { 499 if (h == null || r == null) { 500 return; 501 } 502 503 synchronized (this) { 504 Message p = mMessages; 505 506 // Remove all messages at front. 507 while (p != null && p.target == h && p.callback == r 508 && (object == null || p.obj == object)) { 509 Message n = p.next; 510 mMessages = n; 511 p.recycleUnchecked(); 512 p = n; 513 } 514 515 // Remove all messages after front. 516 while (p != null) { 517 Message n = p.next; 518 if (n != null) { 519 if (n.target == h && n.callback == r 520 && (object == null || n.obj == object)) { 521 Message nn = n.next; 522 n.recycleUnchecked(); 523 p.next = nn; 524 continue; 525 } 526 } 527 p = n; 528 } 529 } 530 } 531 532 void removeCallbacksAndMessages(Handler h, Object object) { 533 if (h == null) { 534 return; 535 } 536 537 synchronized (this) { 538 Message p = mMessages; 539 540 // Remove all messages at front. 541 while (p != null && p.target == h 542 && (object == null || p.obj == object)) { 543 Message n = p.next; 544 mMessages = n; 545 p.recycleUnchecked(); 546 p = n; 547 } 548 549 // Remove all messages after front. 550 while (p != null) { 551 Message n = p.next; 552 if (n != null) { 553 if (n.target == h && (object == null || n.obj == object)) { 554 Message nn = n.next; 555 n.recycleUnchecked(); 556 p.next = nn; 557 continue; 558 } 559 } 560 p = n; 561 } 562 } 563 } 564 565 private void removeAllMessagesLocked() { 566 Message p = mMessages; 567 while (p != null) { 568 Message n = p.next; 569 p.recycleUnchecked(); 570 p = n; 571 } 572 mMessages = null; 573 } 574 575 private void removeAllFutureMessagesLocked() { 576 final long now = SystemClock.uptimeMillis(); 577 Message p = mMessages; 578 if (p != null) { 579 if (p.when > now) { 580 removeAllMessagesLocked(); 581 } else { 582 Message n; 583 for (;;) { 584 n = p.next; 585 if (n == null) { 586 return; 587 } 588 if (n.when > now) { 589 break; 590 } 591 p = n; 592 } 593 p.next = null; 594 do { 595 p = n; 596 n = p.next; 597 p.recycleUnchecked(); 598 } while (n != null); 599 } 600 } 601 } 602 603 void dump(Printer pw, String prefix) { 604 synchronized (this) { 605 long now = SystemClock.uptimeMillis(); 606 int n = 0; 607 for (Message msg = mMessages; msg != null; msg = msg.next) { 608 pw.println(prefix + "Message " + n + ": " + msg.toString(now)); 609 n++; 610 } 611 pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked() 612 + ", quitting=" + mQuitting + ")"); 613 } 614 } 615 616 /** 617 * Callback interface for discovering when a thread is going to block 618 * waiting for more messages. 619 */ 620 public static interface IdleHandler { 621 /** 622 * Called when the message queue has run out of messages and will now 623 * wait for more. Return true to keep your idle handler active, false 624 * to have it removed. This may be called if there are still messages 625 * pending in the queue, but they are all scheduled to be dispatched 626 * after the current time. 627 */ 628 boolean queueIdle(); 629 } 630} 631