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