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