MessageQueue.java revision 67fc67cf3e1dba17b0eae4f3923f3e93a78c7575
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 if (mQuiting) { 136 dispose(); 137 return null; 138 } 139 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 // If first time idle, then get the number of idlers to run. 174 // Idle handles only run if the queue is empty or if the first message 175 // in the queue (possibly a barrier) is due to be handled in the future. 176 if (pendingIdleHandlerCount < 0 177 && (mMessages == null || now < mMessages.when)) { 178 pendingIdleHandlerCount = mIdleHandlers.size(); 179 } 180 if (pendingIdleHandlerCount <= 0) { 181 // No idle handlers to run. Loop and wait some more. 182 mBlocked = true; 183 continue; 184 } 185 186 if (mPendingIdleHandlers == null) { 187 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; 188 } 189 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); 190 } 191 192 // Run the idle handlers. 193 // We only ever reach this code block during the first iteration. 194 for (int i = 0; i < pendingIdleHandlerCount; i++) { 195 final IdleHandler idler = mPendingIdleHandlers[i]; 196 mPendingIdleHandlers[i] = null; // release the reference to the handler 197 198 boolean keep = false; 199 try { 200 keep = idler.queueIdle(); 201 } catch (Throwable t) { 202 Log.wtf("MessageQueue", "IdleHandler threw exception", t); 203 } 204 205 if (!keep) { 206 synchronized (this) { 207 mIdleHandlers.remove(idler); 208 } 209 } 210 } 211 212 // Reset the idle handler count to 0 so we do not run them again. 213 pendingIdleHandlerCount = 0; 214 215 // While calling an idle handler, a new message could have been delivered 216 // so go back and look again for a pending message without waiting. 217 nextPollTimeoutMillis = 0; 218 } 219 } 220 221 void quit() { 222 if (!mQuitAllowed) { 223 throw new RuntimeException("Main thread not allowed to quit."); 224 } 225 226 synchronized (this) { 227 if (mQuiting) { 228 return; 229 } 230 mQuiting = true; 231 } 232 nativeWake(mPtr); 233 } 234 235 int enqueueSyncBarrier(long when) { 236 // Enqueue a new sync barrier token. 237 // We don't need to wake the queue because the purpose of a barrier is to stall it. 238 synchronized (this) { 239 final int token = mNextBarrierToken++; 240 final Message msg = Message.obtain(); 241 msg.arg1 = token; 242 243 Message prev = null; 244 Message p = mMessages; 245 if (when != 0) { 246 while (p != null && p.when <= when) { 247 prev = p; 248 p = p.next; 249 } 250 } 251 if (prev != null) { // invariant: p == prev.next 252 msg.next = p; 253 prev.next = msg; 254 } else { 255 msg.next = p; 256 mMessages = msg; 257 } 258 return token; 259 } 260 } 261 262 void removeSyncBarrier(int token) { 263 // Remove a sync barrier token from the queue. 264 // If the queue is no longer stalled by a barrier then wake it. 265 final boolean needWake; 266 synchronized (this) { 267 Message prev = null; 268 Message p = mMessages; 269 while (p != null && (p.target != null || p.arg1 != token)) { 270 prev = p; 271 p = p.next; 272 } 273 if (p == null) { 274 throw new IllegalStateException("The specified message queue synchronization " 275 + " barrier token has not been posted or has already been removed."); 276 } 277 if (prev != null) { 278 prev.next = p.next; 279 needWake = false; 280 } else { 281 mMessages = p.next; 282 needWake = mMessages == null || mMessages.target != null; 283 } 284 p.recycle(); 285 } 286 if (needWake) { 287 nativeWake(mPtr); 288 } 289 } 290 291 boolean enqueueMessage(Message msg, long when) { 292 if (msg.isInUse()) { 293 throw new AndroidRuntimeException(msg + " This message is already in use."); 294 } 295 if (msg.target == null) { 296 throw new AndroidRuntimeException("Message must have a target."); 297 } 298 299 boolean needWake; 300 synchronized (this) { 301 if (mQuiting) { 302 RuntimeException e = new RuntimeException( 303 msg.target + " sending message to a Handler on a dead thread"); 304 Log.w("MessageQueue", e.getMessage(), e); 305 return false; 306 } 307 308 msg.when = when; 309 Message p = mMessages; 310 if (p == null || when == 0 || when < p.when) { 311 // New head, wake up the event queue if blocked. 312 msg.next = p; 313 mMessages = msg; 314 needWake = mBlocked; 315 } else { 316 // Inserted within the middle of the queue. Usually we don't have to wake 317 // up the event queue unless there is a barrier at the head of the queue 318 // and the message is the earliest asynchronous message in the queue. 319 needWake = mBlocked && p.target == null && msg.isAsynchronous(); 320 Message prev; 321 for (;;) { 322 prev = p; 323 p = p.next; 324 if (p == null || when < p.when) { 325 break; 326 } 327 if (needWake && p.isAsynchronous()) { 328 needWake = false; 329 } 330 } 331 msg.next = p; // invariant: p == prev.next 332 prev.next = msg; 333 } 334 } 335 if (needWake) { 336 nativeWake(mPtr); 337 } 338 return true; 339 } 340 341 boolean hasMessages(Handler h, int what, Object object) { 342 if (h == null) { 343 return false; 344 } 345 346 synchronized (this) { 347 Message p = mMessages; 348 while (p != null) { 349 if (p.target == h && p.what == what && (object == null || p.obj == object)) { 350 return true; 351 } 352 p = p.next; 353 } 354 return false; 355 } 356 } 357 358 boolean hasMessages(Handler h, Runnable r, Object object) { 359 if (h == null) { 360 return false; 361 } 362 363 synchronized (this) { 364 Message p = mMessages; 365 while (p != null) { 366 if (p.target == h && p.callback == r && (object == null || p.obj == object)) { 367 return true; 368 } 369 p = p.next; 370 } 371 return false; 372 } 373 } 374 375 void removeMessages(Handler h, int what, Object object) { 376 if (h == null) { 377 return; 378 } 379 380 synchronized (this) { 381 Message p = mMessages; 382 383 // Remove all messages at front. 384 while (p != null && p.target == h && p.what == what 385 && (object == null || p.obj == object)) { 386 Message n = p.next; 387 mMessages = n; 388 p.recycle(); 389 p = n; 390 } 391 392 // Remove all messages after front. 393 while (p != null) { 394 Message n = p.next; 395 if (n != null) { 396 if (n.target == h && n.what == what 397 && (object == null || n.obj == object)) { 398 Message nn = n.next; 399 n.recycle(); 400 p.next = nn; 401 continue; 402 } 403 } 404 p = n; 405 } 406 } 407 } 408 409 void removeMessages(Handler h, Runnable r, Object object) { 410 if (h == null || r == null) { 411 return; 412 } 413 414 synchronized (this) { 415 Message p = mMessages; 416 417 // Remove all messages at front. 418 while (p != null && p.target == h && p.callback == r 419 && (object == null || p.obj == object)) { 420 Message n = p.next; 421 mMessages = n; 422 p.recycle(); 423 p = n; 424 } 425 426 // Remove all messages after front. 427 while (p != null) { 428 Message n = p.next; 429 if (n != null) { 430 if (n.target == h && n.callback == r 431 && (object == null || n.obj == object)) { 432 Message nn = n.next; 433 n.recycle(); 434 p.next = nn; 435 continue; 436 } 437 } 438 p = n; 439 } 440 } 441 } 442 443 void removeCallbacksAndMessages(Handler h, Object object) { 444 if (h == null) { 445 return; 446 } 447 448 synchronized (this) { 449 Message p = mMessages; 450 451 // Remove all messages at front. 452 while (p != null && p.target == h 453 && (object == null || p.obj == object)) { 454 Message n = p.next; 455 mMessages = n; 456 p.recycle(); 457 p = n; 458 } 459 460 // Remove all messages after front. 461 while (p != null) { 462 Message n = p.next; 463 if (n != null) { 464 if (n.target == h && (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} 476