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 Message mMessages; 34 private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); 35 private IdleHandler[] mPendingIdleHandlers; 36 private boolean mQuiting; 37 boolean mQuitAllowed = true; 38 39 // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout. 40 private boolean mBlocked; 41 42 @SuppressWarnings("unused") 43 private int mPtr; // used by native code 44 nativeInit()45 private native void nativeInit(); nativeDestroy()46 private native void nativeDestroy(); nativePollOnce(int ptr, int timeoutMillis)47 private native void nativePollOnce(int ptr, int timeoutMillis); nativeWake(int ptr)48 private native void nativeWake(int ptr); 49 50 /** 51 * Callback interface for discovering when a thread is going to block 52 * waiting for more messages. 53 */ 54 public static interface IdleHandler { 55 /** 56 * Called when the message queue has run out of messages and will now 57 * wait for more. Return true to keep your idle handler active, false 58 * to have it removed. This may be called if there are still messages 59 * pending in the queue, but they are all scheduled to be dispatched 60 * after the current time. 61 */ queueIdle()62 boolean queueIdle(); 63 } 64 65 /** 66 * Add a new {@link IdleHandler} to this message queue. This may be 67 * removed automatically for you by returning false from 68 * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is 69 * invoked, or explicitly removing it with {@link #removeIdleHandler}. 70 * 71 * <p>This method is safe to call from any thread. 72 * 73 * @param handler The IdleHandler to be added. 74 */ addIdleHandler(IdleHandler handler)75 public final void addIdleHandler(IdleHandler handler) { 76 if (handler == null) { 77 throw new NullPointerException("Can't add a null IdleHandler"); 78 } 79 synchronized (this) { 80 mIdleHandlers.add(handler); 81 } 82 } 83 84 /** 85 * Remove an {@link IdleHandler} from the queue that was previously added 86 * with {@link #addIdleHandler}. If the given object is not currently 87 * in the idle list, nothing is done. 88 * 89 * @param handler The IdleHandler to be removed. 90 */ removeIdleHandler(IdleHandler handler)91 public final void removeIdleHandler(IdleHandler handler) { 92 synchronized (this) { 93 mIdleHandlers.remove(handler); 94 } 95 } 96 MessageQueue()97 MessageQueue() { 98 nativeInit(); 99 } 100 101 @Override finalize()102 protected void finalize() throws Throwable { 103 try { 104 nativeDestroy(); 105 } finally { 106 super.finalize(); 107 } 108 } 109 next()110 final Message next() { 111 int pendingIdleHandlerCount = -1; // -1 only during first iteration 112 int nextPollTimeoutMillis = 0; 113 114 for (;;) { 115 if (nextPollTimeoutMillis != 0) { 116 Binder.flushPendingCommands(); 117 } 118 nativePollOnce(mPtr, nextPollTimeoutMillis); 119 120 synchronized (this) { 121 // Try to retrieve the next message. Return if found. 122 final long now = SystemClock.uptimeMillis(); 123 final Message msg = mMessages; 124 if (msg != null) { 125 final long when = msg.when; 126 if (now >= when) { 127 mBlocked = false; 128 mMessages = msg.next; 129 msg.next = null; 130 if (false) Log.v("MessageQueue", "Returning message: " + msg); 131 msg.markInUse(); 132 return msg; 133 } else { 134 nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE); 135 } 136 } else { 137 nextPollTimeoutMillis = -1; 138 } 139 140 // If first time, then get the number of idlers to run. 141 if (pendingIdleHandlerCount < 0) { 142 pendingIdleHandlerCount = mIdleHandlers.size(); 143 } 144 if (pendingIdleHandlerCount == 0) { 145 // No idle handlers to run. Loop and wait some more. 146 mBlocked = true; 147 continue; 148 } 149 150 if (mPendingIdleHandlers == null) { 151 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; 152 } 153 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); 154 } 155 156 // Run the idle handlers. 157 // We only ever reach this code block during the first iteration. 158 for (int i = 0; i < pendingIdleHandlerCount; i++) { 159 final IdleHandler idler = mPendingIdleHandlers[i]; 160 mPendingIdleHandlers[i] = null; // release the reference to the handler 161 162 boolean keep = false; 163 try { 164 keep = idler.queueIdle(); 165 } catch (Throwable t) { 166 Log.wtf("MessageQueue", "IdleHandler threw exception", t); 167 } 168 169 if (!keep) { 170 synchronized (this) { 171 mIdleHandlers.remove(idler); 172 } 173 } 174 } 175 176 // Reset the idle handler count to 0 so we do not run them again. 177 pendingIdleHandlerCount = 0; 178 179 // While calling an idle handler, a new message could have been delivered 180 // so go back and look again for a pending message without waiting. 181 nextPollTimeoutMillis = 0; 182 } 183 } 184 enqueueMessage(Message msg, long when)185 final boolean enqueueMessage(Message msg, long when) { 186 if (msg.isInUse()) { 187 throw new AndroidRuntimeException(msg 188 + " This message is already in use."); 189 } 190 if (msg.target == null && !mQuitAllowed) { 191 throw new RuntimeException("Main thread not allowed to quit"); 192 } 193 final boolean needWake; 194 synchronized (this) { 195 if (mQuiting) { 196 RuntimeException e = new RuntimeException( 197 msg.target + " sending message to a Handler on a dead thread"); 198 Log.w("MessageQueue", e.getMessage(), e); 199 return false; 200 } else if (msg.target == null) { 201 mQuiting = true; 202 } 203 204 msg.when = when; 205 //Log.d("MessageQueue", "Enqueing: " + msg); 206 Message p = mMessages; 207 if (p == null || when == 0 || when < p.when) { 208 msg.next = p; 209 mMessages = msg; 210 needWake = mBlocked; // new head, might need to wake up 211 } else { 212 Message prev = null; 213 while (p != null && p.when <= when) { 214 prev = p; 215 p = p.next; 216 } 217 msg.next = prev.next; 218 prev.next = msg; 219 needWake = false; // still waiting on head, no need to wake up 220 } 221 } 222 if (needWake) { 223 nativeWake(mPtr); 224 } 225 return true; 226 } 227 removeMessages(Handler h, int what, Object object, boolean doRemove)228 final boolean removeMessages(Handler h, int what, Object object, 229 boolean doRemove) { 230 synchronized (this) { 231 Message p = mMessages; 232 boolean found = false; 233 234 // Remove all messages at front. 235 while (p != null && p.target == h && p.what == what 236 && (object == null || p.obj == object)) { 237 if (!doRemove) return true; 238 found = true; 239 Message n = p.next; 240 mMessages = n; 241 p.recycle(); 242 p = n; 243 } 244 245 // Remove all messages after front. 246 while (p != null) { 247 Message n = p.next; 248 if (n != null) { 249 if (n.target == h && n.what == what 250 && (object == null || n.obj == object)) { 251 if (!doRemove) return true; 252 found = true; 253 Message nn = n.next; 254 n.recycle(); 255 p.next = nn; 256 continue; 257 } 258 } 259 p = n; 260 } 261 262 return found; 263 } 264 } 265 removeMessages(Handler h, Runnable r, Object object)266 final void removeMessages(Handler h, Runnable r, Object object) { 267 if (r == null) { 268 return; 269 } 270 271 synchronized (this) { 272 Message p = mMessages; 273 274 // Remove all messages at front. 275 while (p != null && p.target == h && p.callback == r 276 && (object == null || p.obj == object)) { 277 Message n = p.next; 278 mMessages = n; 279 p.recycle(); 280 p = n; 281 } 282 283 // Remove all messages after front. 284 while (p != null) { 285 Message n = p.next; 286 if (n != null) { 287 if (n.target == h && n.callback == r 288 && (object == null || n.obj == object)) { 289 Message nn = n.next; 290 n.recycle(); 291 p.next = nn; 292 continue; 293 } 294 } 295 p = n; 296 } 297 } 298 } 299 removeCallbacksAndMessages(Handler h, Object object)300 final void removeCallbacksAndMessages(Handler h, Object object) { 301 synchronized (this) { 302 Message p = mMessages; 303 304 // Remove all messages at front. 305 while (p != null && p.target == h 306 && (object == null || p.obj == object)) { 307 Message n = p.next; 308 mMessages = n; 309 p.recycle(); 310 p = n; 311 } 312 313 // Remove all messages after front. 314 while (p != null) { 315 Message n = p.next; 316 if (n != null) { 317 if (n.target == h && (object == null || n.obj == object)) { 318 Message nn = n.next; 319 n.recycle(); 320 p.next = nn; 321 continue; 322 } 323 } 324 p = n; 325 } 326 } 327 } 328 329 /* 330 private void dumpQueue_l() 331 { 332 Message p = mMessages; 333 System.out.println(this + " queue is:"); 334 while (p != null) { 335 System.out.println(" " + p); 336 p = p.next; 337 } 338 } 339 */ 340 } 341