MessageQueue.java revision 6c7b41adf9e937a66880b8906389760f3fc82a08
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 int enqueueSyncBarrier(long when) { 279 // Enqueue a new sync barrier token. 280 // We don't need to wake the queue because the purpose of a barrier is to stall it. 281 synchronized (this) { 282 final int token = mNextBarrierToken++; 283 final Message msg = Message.obtain(); 284 msg.markInUse(); 285 msg.when = when; 286 msg.arg1 = token; 287 288 Message prev = null; 289 Message p = mMessages; 290 if (when != 0) { 291 while (p != null && p.when <= when) { 292 prev = p; 293 p = p.next; 294 } 295 } 296 if (prev != null) { // invariant: p == prev.next 297 msg.next = p; 298 prev.next = msg; 299 } else { 300 msg.next = p; 301 mMessages = msg; 302 } 303 return token; 304 } 305 } 306 307 void removeSyncBarrier(int token) { 308 // Remove a sync barrier token from the queue. 309 // If the queue is no longer stalled by a barrier then wake it. 310 synchronized (this) { 311 Message prev = null; 312 Message p = mMessages; 313 while (p != null && (p.target != null || p.arg1 != token)) { 314 prev = p; 315 p = p.next; 316 } 317 if (p == null) { 318 throw new IllegalStateException("The specified message queue synchronization " 319 + " barrier token has not been posted or has already been removed."); 320 } 321 final boolean needWake; 322 if (prev != null) { 323 prev.next = p.next; 324 needWake = false; 325 } else { 326 mMessages = p.next; 327 needWake = mMessages == null || mMessages.target != null; 328 } 329 p.recycleUnchecked(); 330 331 // If the loop is quitting then it is already awake. 332 // We can assume mPtr != 0 when mQuitting is false. 333 if (needWake && !mQuitting) { 334 nativeWake(mPtr); 335 } 336 } 337 } 338 339 boolean enqueueMessage(Message msg, long when) { 340 if (msg.target == null) { 341 throw new IllegalArgumentException("Message must have a target."); 342 } 343 if (msg.isInUse()) { 344 throw new IllegalStateException(msg + " This message is already in use."); 345 } 346 347 synchronized (this) { 348 if (mQuitting) { 349 IllegalStateException e = new IllegalStateException( 350 msg.target + " sending message to a Handler on a dead thread"); 351 Log.w("MessageQueue", e.getMessage(), e); 352 msg.recycle(); 353 return false; 354 } 355 356 msg.markInUse(); 357 msg.when = when; 358 Message p = mMessages; 359 boolean needWake; 360 if (p == null || when == 0 || when < p.when) { 361 // New head, wake up the event queue if blocked. 362 msg.next = p; 363 mMessages = msg; 364 needWake = mBlocked; 365 } else { 366 // Inserted within the middle of the queue. Usually we don't have to wake 367 // up the event queue unless there is a barrier at the head of the queue 368 // and the message is the earliest asynchronous message in the queue. 369 needWake = mBlocked && p.target == null && msg.isAsynchronous(); 370 Message prev; 371 for (;;) { 372 prev = p; 373 p = p.next; 374 if (p == null || when < p.when) { 375 break; 376 } 377 if (needWake && p.isAsynchronous()) { 378 needWake = false; 379 } 380 } 381 msg.next = p; // invariant: p == prev.next 382 prev.next = msg; 383 } 384 385 // We can assume mPtr != 0 because mQuitting is false. 386 if (needWake) { 387 nativeWake(mPtr); 388 } 389 } 390 return true; 391 } 392 393 boolean hasMessages(Handler h, int what, Object object) { 394 if (h == null) { 395 return false; 396 } 397 398 synchronized (this) { 399 Message p = mMessages; 400 while (p != null) { 401 if (p.target == h && p.what == what && (object == null || p.obj == object)) { 402 return true; 403 } 404 p = p.next; 405 } 406 return false; 407 } 408 } 409 410 boolean hasMessages(Handler h, Runnable r, Object object) { 411 if (h == null) { 412 return false; 413 } 414 415 synchronized (this) { 416 Message p = mMessages; 417 while (p != null) { 418 if (p.target == h && p.callback == r && (object == null || p.obj == object)) { 419 return true; 420 } 421 p = p.next; 422 } 423 return false; 424 } 425 } 426 427 void removeMessages(Handler h, int what, Object object) { 428 if (h == null) { 429 return; 430 } 431 432 synchronized (this) { 433 Message p = mMessages; 434 435 // Remove all messages at front. 436 while (p != null && p.target == h && p.what == what 437 && (object == null || p.obj == object)) { 438 Message n = p.next; 439 mMessages = n; 440 p.recycleUnchecked(); 441 p = n; 442 } 443 444 // Remove all messages after front. 445 while (p != null) { 446 Message n = p.next; 447 if (n != null) { 448 if (n.target == h && n.what == what 449 && (object == null || n.obj == object)) { 450 Message nn = n.next; 451 n.recycleUnchecked(); 452 p.next = nn; 453 continue; 454 } 455 } 456 p = n; 457 } 458 } 459 } 460 461 void removeMessages(Handler h, Runnable r, Object object) { 462 if (h == null || r == null) { 463 return; 464 } 465 466 synchronized (this) { 467 Message p = mMessages; 468 469 // Remove all messages at front. 470 while (p != null && p.target == h && p.callback == r 471 && (object == null || p.obj == object)) { 472 Message n = p.next; 473 mMessages = n; 474 p.recycleUnchecked(); 475 p = n; 476 } 477 478 // Remove all messages after front. 479 while (p != null) { 480 Message n = p.next; 481 if (n != null) { 482 if (n.target == h && n.callback == r 483 && (object == null || n.obj == object)) { 484 Message nn = n.next; 485 n.recycleUnchecked(); 486 p.next = nn; 487 continue; 488 } 489 } 490 p = n; 491 } 492 } 493 } 494 495 void removeCallbacksAndMessages(Handler h, Object object) { 496 if (h == null) { 497 return; 498 } 499 500 synchronized (this) { 501 Message p = mMessages; 502 503 // Remove all messages at front. 504 while (p != null && p.target == h 505 && (object == null || p.obj == object)) { 506 Message n = p.next; 507 mMessages = n; 508 p.recycleUnchecked(); 509 p = n; 510 } 511 512 // Remove all messages after front. 513 while (p != null) { 514 Message n = p.next; 515 if (n != null) { 516 if (n.target == h && (object == null || n.obj == object)) { 517 Message nn = n.next; 518 n.recycleUnchecked(); 519 p.next = nn; 520 continue; 521 } 522 } 523 p = n; 524 } 525 } 526 } 527 528 private void removeAllMessagesLocked() { 529 Message p = mMessages; 530 while (p != null) { 531 Message n = p.next; 532 p.recycleUnchecked(); 533 p = n; 534 } 535 mMessages = null; 536 } 537 538 private void removeAllFutureMessagesLocked() { 539 final long now = SystemClock.uptimeMillis(); 540 Message p = mMessages; 541 if (p != null) { 542 if (p.when > now) { 543 removeAllMessagesLocked(); 544 } else { 545 Message n; 546 for (;;) { 547 n = p.next; 548 if (n == null) { 549 return; 550 } 551 if (n.when > now) { 552 break; 553 } 554 p = n; 555 } 556 p.next = null; 557 do { 558 p = n; 559 n = p.next; 560 p.recycleUnchecked(); 561 } while (n != null); 562 } 563 } 564 } 565 566 void dump(Printer pw, String prefix) { 567 synchronized (this) { 568 long now = SystemClock.uptimeMillis(); 569 int n = 0; 570 for (Message msg = mMessages; msg != null; msg = msg.next) { 571 pw.println(prefix + "Message " + n + ": " + msg.toString(now)); 572 n++; 573 } 574 pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked() 575 + ", quitting=" + mQuitting + ")"); 576 } 577 } 578 579 /** 580 * Callback interface for discovering when a thread is going to block 581 * waiting for more messages. 582 */ 583 public static interface IdleHandler { 584 /** 585 * Called when the message queue has run out of messages and will now 586 * wait for more. Return true to keep your idle handler active, false 587 * to have it removed. This may be called if there are still messages 588 * pending in the queue, but they are all scheduled to be dispatched 589 * after the current time. 590 */ 591 boolean queueIdle(); 592 } 593} 594