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