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