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