blob: bb078253aec58e68c7775a7f7f39ab5b8408e5a6 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.util.AndroidRuntimeException;
20import android.util.Config;
21import android.util.Log;
22
Jeff Brown46b9ac02010-04-22 18:58:52 -070023import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024
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;
Jeff Brown46b9ac02010-04-22 18:58:52 -070035 private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
Jeff Browned739322010-09-21 15:14:14 -070036 private IdleHandler[] mPendingIdleHandlers;
Jeff Brown415d8c32010-10-05 15:35:37 -070037 private boolean mQuiting;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038 boolean mQuitAllowed = true;
Christopher Tatefa9e7c02010-05-06 12:07:10 -070039
Jeff Brown415d8c32010-10-05 15:35:37 -070040 // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
41 private boolean mBlocked;
42
Jeff Brown46b9ac02010-04-22 18:58:52 -070043 @SuppressWarnings("unused")
44 private int mPtr; // used by native code
45
46 private native void nativeInit();
47 private native void nativeDestroy();
Jeff Brown415d8c32010-10-05 15:35:37 -070048 private native void nativePollOnce(int ptr, int timeoutMillis);
49 private native void nativeWake(int ptr);
Jeff Brown46b9ac02010-04-22 18:58:52 -070050
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 /**
52 * Callback interface for discovering when a thread is going to block
53 * waiting for more messages.
54 */
55 public static interface IdleHandler {
56 /**
57 * Called when the message queue has run out of messages and will now
58 * wait for more. Return true to keep your idle handler active, false
59 * to have it removed. This may be called if there are still messages
60 * pending in the queue, but they are all scheduled to be dispatched
61 * after the current time.
62 */
63 boolean queueIdle();
64 }
65
66 /**
67 * Add a new {@link IdleHandler} to this message queue. This may be
68 * removed automatically for you by returning false from
69 * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
70 * invoked, or explicitly removing it with {@link #removeIdleHandler}.
71 *
72 * <p>This method is safe to call from any thread.
73 *
74 * @param handler The IdleHandler to be added.
75 */
76 public final void addIdleHandler(IdleHandler handler) {
77 if (handler == null) {
78 throw new NullPointerException("Can't add a null IdleHandler");
79 }
80 synchronized (this) {
81 mIdleHandlers.add(handler);
82 }
83 }
84
85 /**
86 * Remove an {@link IdleHandler} from the queue that was previously added
87 * with {@link #addIdleHandler}. If the given object is not currently
88 * in the idle list, nothing is done.
89 *
90 * @param handler The IdleHandler to be removed.
91 */
92 public final void removeIdleHandler(IdleHandler handler) {
93 synchronized (this) {
94 mIdleHandlers.remove(handler);
95 }
96 }
Jeff Brown46b9ac02010-04-22 18:58:52 -070097
Christopher Tatefa9e7c02010-05-06 12:07:10 -070098 MessageQueue() {
99 nativeInit();
100 }
Jeff Brown46b9ac02010-04-22 18:58:52 -0700101
102 @Override
103 protected void finalize() throws Throwable {
104 try {
105 nativeDestroy();
106 } finally {
107 super.finalize();
108 }
109 }
Christopher Tatefa9e7c02010-05-06 12:07:10 -0700110
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 final Message next() {
Jeff Browned739322010-09-21 15:14:14 -0700112 int pendingIdleHandlerCount = -1; // -1 only during first iteration
113 int nextPollTimeoutMillis = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114
Jeff Browned739322010-09-21 15:14:14 -0700115 for (;;) {
116 if (nextPollTimeoutMillis != 0) {
117 Binder.flushPendingCommands();
118 }
Jeff Brown415d8c32010-10-05 15:35:37 -0700119 nativePollOnce(mPtr, nextPollTimeoutMillis);
Christopher Tatefa9e7c02010-05-06 12:07:10 -0700120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 synchronized (this) {
Jeff Browned739322010-09-21 15:14:14 -0700122 // Try to retrieve the next message. Return if found.
123 final long now = SystemClock.uptimeMillis();
124 final Message msg = mMessages;
Jeff Brown46b9ac02010-04-22 18:58:52 -0700125 if (msg != null) {
Jeff Browned739322010-09-21 15:14:14 -0700126 final long when = msg.when;
127 if (now >= when) {
Jeff Brown415d8c32010-10-05 15:35:37 -0700128 mBlocked = false;
Jeff Browned739322010-09-21 15:14:14 -0700129 mMessages = msg.next;
Jeff Brown415d8c32010-10-05 15:35:37 -0700130 msg.next = null;
Jeff Browned739322010-09-21 15:14:14 -0700131 if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
Jeff Brown85c3fe42010-09-21 16:45:16 -0700132 msg.markInUse();
Jeff Browned739322010-09-21 15:14:14 -0700133 return msg;
Jeff Brown46b9ac02010-04-22 18:58:52 -0700134 } else {
Jeff Browned739322010-09-21 15:14:14 -0700135 nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 }
Christopher Tatefa9e7c02010-05-06 12:07:10 -0700137 } else {
Jeff Browned739322010-09-21 15:14:14 -0700138 nextPollTimeoutMillis = -1;
139 }
140
141 // If first time, then get the number of idlers to run.
142 if (pendingIdleHandlerCount < 0) {
143 pendingIdleHandlerCount = mIdleHandlers.size();
144 }
145 if (pendingIdleHandlerCount == 0) {
146 // No idle handlers to run. Loop and wait some more.
Jeff Brown415d8c32010-10-05 15:35:37 -0700147 mBlocked = true;
Jeff Browned739322010-09-21 15:14:14 -0700148 continue;
149 }
150
151 if (mPendingIdleHandlers == null) {
152 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
153 }
154 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
155 }
156
157 // Run the idle handlers.
158 // We only ever reach this code block during the first iteration.
159 for (int i = 0; i < pendingIdleHandlerCount; i++) {
160 final IdleHandler idler = mPendingIdleHandlers[i];
161 mPendingIdleHandlers[i] = null; // release the reference to the handler
162
163 boolean keep = false;
164 try {
165 keep = idler.queueIdle();
166 } catch (Throwable t) {
167 Log.wtf("MessageQueue", "IdleHandler threw exception", t);
168 }
169
170 if (!keep) {
171 synchronized (this) {
172 mIdleHandlers.remove(idler);
173 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 }
175 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176
Jeff Browned739322010-09-21 15:14:14 -0700177 // Reset the idle handler count to 0 so we do not run them again.
178 pendingIdleHandlerCount = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179
Jeff Browned739322010-09-21 15:14:14 -0700180 // While calling an idle handler, a new message could have been delivered
181 // so go back and look again for a pending message without waiting.
182 nextPollTimeoutMillis = 0;
183 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 }
185
186 final boolean enqueueMessage(Message msg, long when) {
Wink Savillea334e7c2010-08-24 10:56:30 -0700187 if (msg.isInUse()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 throw new AndroidRuntimeException(msg
189 + " This message is already in use.");
190 }
191 if (msg.target == null && !mQuitAllowed) {
192 throw new RuntimeException("Main thread not allowed to quit");
193 }
Jeff Brown415d8c32010-10-05 15:35:37 -0700194 final boolean needWake;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 synchronized (this) {
196 if (mQuiting) {
197 RuntimeException e = new RuntimeException(
198 msg.target + " sending message to a Handler on a dead thread");
199 Log.w("MessageQueue", e.getMessage(), e);
200 return false;
201 } else if (msg.target == null) {
202 mQuiting = true;
203 }
204
205 msg.when = when;
206 //Log.d("MessageQueue", "Enqueing: " + msg);
207 Message p = mMessages;
208 if (p == null || when == 0 || when < p.when) {
209 msg.next = p;
210 mMessages = msg;
Jeff Brown415d8c32010-10-05 15:35:37 -0700211 needWake = mBlocked; // new head, might need to wake up
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 } else {
213 Message prev = null;
214 while (p != null && p.when <= when) {
215 prev = p;
216 p = p.next;
217 }
218 msg.next = prev.next;
219 prev.next = msg;
Jeff Brown415d8c32010-10-05 15:35:37 -0700220 needWake = false; // still waiting on head, no need to wake up
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 }
222 }
Jeff Brown415d8c32010-10-05 15:35:37 -0700223 if (needWake) {
224 nativeWake(mPtr);
225 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 return true;
227 }
228
229 final boolean removeMessages(Handler h, int what, Object object,
230 boolean doRemove) {
231 synchronized (this) {
232 Message p = mMessages;
233 boolean found = false;
234
235 // Remove all messages at front.
236 while (p != null && p.target == h && p.what == what
237 && (object == null || p.obj == object)) {
238 if (!doRemove) return true;
239 found = true;
240 Message n = p.next;
241 mMessages = n;
242 p.recycle();
243 p = n;
244 }
245
246 // Remove all messages after front.
247 while (p != null) {
248 Message n = p.next;
249 if (n != null) {
250 if (n.target == h && n.what == what
251 && (object == null || n.obj == object)) {
252 if (!doRemove) return true;
253 found = true;
254 Message nn = n.next;
255 n.recycle();
256 p.next = nn;
257 continue;
258 }
259 }
260 p = n;
261 }
262
263 return found;
264 }
265 }
266
267 final void removeMessages(Handler h, Runnable r, Object object) {
268 if (r == null) {
269 return;
270 }
271
272 synchronized (this) {
273 Message p = mMessages;
274
275 // Remove all messages at front.
276 while (p != null && p.target == h && p.callback == r
277 && (object == null || p.obj == object)) {
278 Message n = p.next;
279 mMessages = n;
280 p.recycle();
281 p = n;
282 }
283
284 // Remove all messages after front.
285 while (p != null) {
286 Message n = p.next;
287 if (n != null) {
288 if (n.target == h && n.callback == r
289 && (object == null || n.obj == object)) {
290 Message nn = n.next;
291 n.recycle();
292 p.next = nn;
293 continue;
294 }
295 }
296 p = n;
297 }
298 }
299 }
300
301 final void removeCallbacksAndMessages(Handler h, Object object) {
302 synchronized (this) {
303 Message p = mMessages;
304
305 // Remove all messages at front.
306 while (p != null && p.target == h
307 && (object == null || p.obj == object)) {
308 Message n = p.next;
309 mMessages = n;
310 p.recycle();
311 p = n;
312 }
313
314 // Remove all messages after front.
315 while (p != null) {
316 Message n = p.next;
317 if (n != null) {
318 if (n.target == h && (object == null || n.obj == object)) {
319 Message nn = n.next;
320 n.recycle();
321 p.next = nn;
322 continue;
323 }
324 }
325 p = n;
326 }
327 }
328 }
329
330 /*
331 private void dumpQueue_l()
332 {
333 Message p = mMessages;
334 System.out.println(this + " queue is:");
335 while (p != null) {
336 System.out.println(" " + p);
337 p = p.next;
338 }
339 }
340 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341}