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