MessageQueue.java revision 024136f57e5f8b4c11a4fe0fd83061eb6d372d26
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; 21 22import 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 */ 32public 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() { 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 nativeWake(mPtr); 234 } 235 236 int enqueueSyncBarrier(long when) { 237 // Enqueue a new sync barrier token. 238 // We don't need to wake the queue because the purpose of a barrier is to stall it. 239 synchronized (this) { 240 final int token = mNextBarrierToken++; 241 final Message msg = Message.obtain(); 242 msg.arg1 = token; 243 244 Message prev = null; 245 Message p = mMessages; 246 if (when != 0) { 247 while (p != null && p.when <= when) { 248 prev = p; 249 p = p.next; 250 } 251 } 252 if (prev != null) { // invariant: p == prev.next 253 msg.next = p; 254 prev.next = msg; 255 } else { 256 msg.next = p; 257 mMessages = msg; 258 } 259 return token; 260 } 261 } 262 263 void removeSyncBarrier(int token) { 264 // Remove a sync barrier token from the queue. 265 // If the queue is no longer stalled by a barrier then wake it. 266 final boolean needWake; 267 synchronized (this) { 268 Message prev = null; 269 Message p = mMessages; 270 while (p != null && (p.target != null || p.arg1 != token)) { 271 prev = p; 272 p = p.next; 273 } 274 if (p == null) { 275 throw new IllegalStateException("The specified message queue synchronization " 276 + " barrier token has not been posted or has already been removed."); 277 } 278 if (prev != null) { 279 prev.next = p.next; 280 needWake = false; 281 } else { 282 mMessages = p.next; 283 needWake = mMessages == null || mMessages.target != null; 284 } 285 p.recycle(); 286 } 287 if (needWake) { 288 nativeWake(mPtr); 289 } 290 } 291 292 boolean enqueueMessage(Message msg, long when) { 293 if (msg.isInUse()) { 294 throw new AndroidRuntimeException(msg + " This message is already in use."); 295 } 296 if (msg.target == null) { 297 throw new AndroidRuntimeException("Message must have a target."); 298 } 299 300 boolean needWake; 301 synchronized (this) { 302 if (mQuiting) { 303 RuntimeException e = new RuntimeException( 304 msg.target + " sending message to a Handler on a dead thread"); 305 Log.w("MessageQueue", e.getMessage(), e); 306 return false; 307 } 308 309 msg.when = when; 310 Message p = mMessages; 311 if (p == null || when == 0 || when < p.when) { 312 // New head, wake up the event queue if blocked. 313 msg.next = p; 314 mMessages = msg; 315 needWake = mBlocked; 316 } else { 317 // Inserted within the middle of the queue. Usually we don't have to wake 318 // up the event queue unless there is a barrier at the head of the queue 319 // and the message is the earliest asynchronous message in the queue. 320 needWake = mBlocked && p.target == null && msg.isAsynchronous(); 321 Message prev; 322 for (;;) { 323 prev = p; 324 p = p.next; 325 if (p == null || when < p.when) { 326 break; 327 } 328 if (needWake && p.isAsynchronous()) { 329 needWake = false; 330 } 331 } 332 msg.next = p; // invariant: p == prev.next 333 prev.next = msg; 334 } 335 } 336 if (needWake) { 337 nativeWake(mPtr); 338 } 339 return true; 340 } 341 342 boolean hasMessages(Handler h, int what, Object object) { 343 if (h == null) { 344 return false; 345 } 346 347 synchronized (this) { 348 Message p = mMessages; 349 while (p != null) { 350 if (p.target == h && p.what == what && (object == null || p.obj == object)) { 351 return true; 352 } 353 p = p.next; 354 } 355 return false; 356 } 357 } 358 359 boolean hasMessages(Handler h, Runnable r, Object object) { 360 if (h == null) { 361 return false; 362 } 363 364 synchronized (this) { 365 Message p = mMessages; 366 while (p != null) { 367 if (p.target == h && p.callback == r && (object == null || p.obj == object)) { 368 return true; 369 } 370 p = p.next; 371 } 372 return false; 373 } 374 } 375 376 void removeMessages(Handler h, int what, Object object) { 377 if (h == null) { 378 return; 379 } 380 381 synchronized (this) { 382 Message p = mMessages; 383 384 // Remove all messages at front. 385 while (p != null && p.target == h && p.what == what 386 && (object == null || p.obj == object)) { 387 Message n = p.next; 388 mMessages = n; 389 p.recycle(); 390 p = n; 391 } 392 393 // Remove all messages after front. 394 while (p != null) { 395 Message n = p.next; 396 if (n != null) { 397 if (n.target == h && n.what == what 398 && (object == null || n.obj == object)) { 399 Message nn = n.next; 400 n.recycle(); 401 p.next = nn; 402 continue; 403 } 404 } 405 p = n; 406 } 407 } 408 } 409 410 void removeMessages(Handler h, Runnable r, Object object) { 411 if (h == null || r == null) { 412 return; 413 } 414 415 synchronized (this) { 416 Message p = mMessages; 417 418 // Remove all messages at front. 419 while (p != null && p.target == h && p.callback == r 420 && (object == null || p.obj == object)) { 421 Message n = p.next; 422 mMessages = n; 423 p.recycle(); 424 p = n; 425 } 426 427 // Remove all messages after front. 428 while (p != null) { 429 Message n = p.next; 430 if (n != null) { 431 if (n.target == h && n.callback == r 432 && (object == null || n.obj == object)) { 433 Message nn = n.next; 434 n.recycle(); 435 p.next = nn; 436 continue; 437 } 438 } 439 p = n; 440 } 441 } 442 } 443 444 void removeCallbacksAndMessages(Handler h, Object object) { 445 if (h == null) { 446 return; 447 } 448 449 synchronized (this) { 450 Message p = mMessages; 451 452 // Remove all messages at front. 453 while (p != null && p.target == h 454 && (object == null || p.obj == object)) { 455 Message n = p.next; 456 mMessages = n; 457 p.recycle(); 458 p = n; 459 } 460 461 // Remove all messages after front. 462 while (p != null) { 463 Message n = p.next; 464 if (n != null) { 465 if (n.target == h && (object == null || n.obj == object)) { 466 Message nn = n.next; 467 n.recycle(); 468 p.next = nn; 469 continue; 470 } 471 } 472 p = n; 473 } 474 } 475 } 476} 477