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