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