blob: d217cab45e361c20837541801833877dcd642f0f [file] [log] [blame]
Jeff Brown96e942d2011-11-30 19:55:01 -08001/*
2 * Copyright (C) 2011 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.view;
18
Jeff Brown96e942d2011-11-30 19:55:01 -080019import android.os.Handler;
20import android.os.Looper;
21import android.os.Message;
22import android.os.SystemClock;
23import android.os.SystemProperties;
24import android.util.Log;
25
26/**
Dianne Hackborn8bcd54b2012-01-31 19:04:53 -080027 * Coordinates animations and drawing for UI on a particular thread.
Jeff Brown87d0b032012-02-03 11:01:21 -080028 *
Jeff Brown4a06c802012-02-15 15:06:01 -080029 * This object is thread-safe. Other threads can post callbacks to run at a later time
30 * on the UI thread.
Jeff Brown87d0b032012-02-03 11:01:21 -080031 *
Jeff Brown58aedbc2012-02-13 20:15:01 -080032 * Ensuring thread-safety is a little tricky because the {@link DisplayEventReceiver}
33 * can only be accessed from the UI thread so operations that touch the event receiver
34 * are posted to the UI thread if needed.
35 *
Jeff Brown96e942d2011-11-30 19:55:01 -080036 * @hide
37 */
Jeff Brown96858852012-02-14 13:45:06 -080038public final class Choreographer {
Jeff Brown96e942d2011-11-30 19:55:01 -080039 private static final String TAG = "Choreographer";
40 private static final boolean DEBUG = false;
41
42 // The default amount of time in ms between animation frames.
43 // When vsync is not enabled, we want to have some idea of how long we should
44 // wait before posting the next animation message. It is important that the
45 // default value be less than the true inter-frame delay on all devices to avoid
46 // situations where we might skip frames by waiting too long (we must compensate
47 // for jitter and hardware variations). Regardless of this value, the animation
48 // and display loop is ultimately rate-limited by how fast new graphics buffers can
49 // be dequeued.
50 private static final long DEFAULT_FRAME_DELAY = 10;
51
52 // The number of milliseconds between animation frames.
Jeff Brown87d0b032012-02-03 11:01:21 -080053 private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY;
Jeff Brown96e942d2011-11-30 19:55:01 -080054
55 // Thread local storage for the choreographer.
56 private static final ThreadLocal<Choreographer> sThreadInstance =
57 new ThreadLocal<Choreographer>() {
58 @Override
59 protected Choreographer initialValue() {
60 Looper looper = Looper.myLooper();
61 if (looper == null) {
62 throw new IllegalStateException("The current thread must have a looper!");
63 }
64 return new Choreographer(looper);
65 }
66 };
67
68 // System property to enable/disable vsync for animations and drawing.
69 // Enabled by default.
70 private static final boolean USE_VSYNC = SystemProperties.getBoolean(
71 "debug.choreographer.vsync", true);
72
73 // System property to enable/disable the use of the vsync / animation timer
74 // for drawing rather than drawing immediately.
Jeff Brown90a3c5f2011-12-06 18:01:37 -080075 // Temporarily disabled by default because postponing performTraversals() violates
76 // assumptions about traversals happening in-order relative to other posted messages.
77 // Bug: 5721047
Jeff Brown96e942d2011-11-30 19:55:01 -080078 private static final boolean USE_ANIMATION_TIMER_FOR_DRAW = SystemProperties.getBoolean(
Jeff Brown90a3c5f2011-12-06 18:01:37 -080079 "debug.choreographer.animdraw", false);
Jeff Brown96e942d2011-11-30 19:55:01 -080080
81 private static final int MSG_DO_ANIMATION = 0;
82 private static final int MSG_DO_DRAW = 1;
Jeff Brown58aedbc2012-02-13 20:15:01 -080083 private static final int MSG_DO_SCHEDULE_VSYNC = 2;
Jeff Brown7ae9d5f2012-03-05 19:33:49 -080084 private static final int MSG_DO_SCHEDULE_ANIMATION = 3;
85 private static final int MSG_DO_SCHEDULE_DRAW = 4;
Jeff Brown96e942d2011-11-30 19:55:01 -080086
Jeff Brown87d0b032012-02-03 11:01:21 -080087 private final Object mLock = new Object();
88
Jeff Brown96e942d2011-11-30 19:55:01 -080089 private final Looper mLooper;
Jeff Brown96858852012-02-14 13:45:06 -080090 private final FrameHandler mHandler;
Jeff Brown1654d0b2012-02-15 15:40:52 -080091 private final FrameDisplayEventReceiver mDisplayEventReceiver;
Jeff Brown96858852012-02-14 13:45:06 -080092
93 private Callback mCallbackPool;
Jeff Brown96e942d2011-11-30 19:55:01 -080094
Jeff Brown43ea54b2012-03-09 14:37:48 -080095 private final CallbackQueue mAnimationCallbackQueue = new CallbackQueue();
96 private final CallbackQueue mDrawCallbackQueue = new CallbackQueue();
Jeff Brown96858852012-02-14 13:45:06 -080097
Jeff Brown96e942d2011-11-30 19:55:01 -080098 private boolean mAnimationScheduled;
99 private boolean mDrawScheduled;
Jeff Brown96e942d2011-11-30 19:55:01 -0800100 private long mLastAnimationTime;
101 private long mLastDrawTime;
102
103 private Choreographer(Looper looper) {
Jeff Brown96e942d2011-11-30 19:55:01 -0800104 mLooper = looper;
Jeff Brown96858852012-02-14 13:45:06 -0800105 mHandler = new FrameHandler(looper);
Jeff Brown1654d0b2012-02-15 15:40:52 -0800106 mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
Jeff Brown96e942d2011-11-30 19:55:01 -0800107 mLastAnimationTime = Long.MIN_VALUE;
108 mLastDrawTime = Long.MIN_VALUE;
109 }
110
111 /**
Dianne Hackborn8bcd54b2012-01-31 19:04:53 -0800112 * Gets the choreographer for the calling thread. Must be called from
113 * a thread that already has a {@link android.os.Looper} associated with it.
Jeff Brown96e942d2011-11-30 19:55:01 -0800114 *
115 * @return The choreographer for this thread.
116 * @throws IllegalStateException if the thread does not have a looper.
117 */
118 public static Choreographer getInstance() {
119 return sThreadInstance.get();
120 }
121
122 /**
123 * The amount of time, in milliseconds, between each frame of the animation. This is a
124 * requested time that the animation will attempt to honor, but the actual delay between
125 * frames may be different, depending on system load and capabilities. This is a static
126 * function because the same delay will be applied to all animations, since they are all
127 * run off of a single timing loop.
128 *
129 * The frame delay may be ignored when the animation system uses an external timing
130 * source, such as the display refresh rate (vsync), to govern animations.
131 *
132 * @return the requested time between frames, in milliseconds
133 */
134 public static long getFrameDelay() {
135 return sFrameDelay;
136 }
137
138 /**
139 * The amount of time, in milliseconds, between each frame of the animation. This is a
140 * requested time that the animation will attempt to honor, but the actual delay between
141 * frames may be different, depending on system load and capabilities. This is a static
142 * function because the same delay will be applied to all animations, since they are all
143 * run off of a single timing loop.
144 *
145 * The frame delay may be ignored when the animation system uses an external timing
146 * source, such as the display refresh rate (vsync), to govern animations.
147 *
148 * @param frameDelay the requested time between frames, in milliseconds
149 */
150 public static void setFrameDelay(long frameDelay) {
151 sFrameDelay = frameDelay;
152 }
153
154 /**
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800155 * Subtracts typical frame delay time from a delay interval in milliseconds.
156 *
157 * This method can be used to compensate for animation delay times that have baked
158 * in assumptions about the frame delay. For example, it's quite common for code to
159 * assume a 60Hz frame time and bake in a 16ms delay. When we call
160 * {@link #postAnimationCallbackDelayed} we want to know how long to wait before
161 * posting the animation callback but let the animation timer take care of the remaining
162 * frame delay time.
163 *
164 * This method is somewhat conservative about how much of the frame delay it
165 * subtracts. It uses the same value returned by {@link #getFrameDelay} which by
166 * default is 10ms even though many parts of the system assume 16ms. Consequently,
167 * we might still wait 6ms before posting an animation callback that we want to run
168 * on the next frame, but this is much better than waiting a whole 16ms and likely
169 * missing the deadline.
170 *
171 * @param delayMillis The original delay time including an assumed frame delay.
172 * @return The adjusted delay time with the assumed frame delay subtracted out.
173 */
174 public static long subtractFrameDelay(long delayMillis) {
175 final long frameDelay = sFrameDelay;
176 return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay;
177 }
178
179 /**
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800180 * Posts a callback to run on the next animation cycle.
Jeff Brown4a06c802012-02-15 15:06:01 -0800181 * The callback only runs once and then is automatically removed.
182 *
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800183 * @param action The callback action to run during the next animation cycle.
184 * @param token The callback token, or null if none.
Jeff Brown4a06c802012-02-15 15:06:01 -0800185 *
186 * @see #removeAnimationCallback
Jeff Brown96e942d2011-11-30 19:55:01 -0800187 */
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800188 public void postAnimationCallback(Runnable action, Object token) {
189 postAnimationCallbackDelayed(action, token, 0);
Jeff Brown87d0b032012-02-03 11:01:21 -0800190 }
191
Jeff Brown4a06c802012-02-15 15:06:01 -0800192 /**
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800193 * Posts a callback to run on the next animation cycle following the specified delay.
194 * The callback only runs once and then is automatically removed.
195 *
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800196 * @param action The callback action to run during the next animation cycle after
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800197 * the specified delay.
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800198 * @param token The callback token, or null if none.
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800199 * @param delayMillis The delay time in milliseconds.
200 *
201 * @see #removeAnimationCallback
202 */
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800203 public void postAnimationCallbackDelayed(Runnable action, Object token, long delayMillis) {
204 if (action == null) {
205 throw new IllegalArgumentException("action must not be null");
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800206 }
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800207
Jeff Brown43ea54b2012-03-09 14:37:48 -0800208 if (DEBUG) {
209 Log.d(TAG, "PostAnimationCallback: " + action + ", token=" + token
210 + ", delayMillis=" + delayMillis);
211 }
212
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800213 synchronized (mLock) {
214 final long now = SystemClock.uptimeMillis();
215 final long dueTime = now + delayMillis;
Jeff Brown43ea54b2012-03-09 14:37:48 -0800216 mAnimationCallbackQueue.addCallbackLocked(dueTime, action, token);
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800217
218 if (dueTime <= now) {
219 scheduleAnimationLocked(now);
220 } else {
221 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_ANIMATION, action);
222 mHandler.sendMessageAtTime(msg, dueTime);
223 }
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800224 }
225 }
226
227 /**
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800228 * Removes animation callbacks that have the specified action and token.
Jeff Brown4a06c802012-02-15 15:06:01 -0800229 *
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800230 * @param action The action property of the callbacks to remove, or null to remove
231 * callbacks with any action.
232 * @param token The token property of the callbacks to remove, or null to remove
233 * callbacks with any token.
Jeff Brown4a06c802012-02-15 15:06:01 -0800234 *
235 * @see #postAnimationCallback
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800236 * @see #postAnimationCallbackDelayed
Jeff Brown4a06c802012-02-15 15:06:01 -0800237 */
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800238 public void removeAnimationCallbacks(Runnable action, Object token) {
Jeff Brown43ea54b2012-03-09 14:37:48 -0800239 if (DEBUG) {
240 Log.d(TAG, "RemoveAnimationCallbacks: " + action + ", token=" + token);
241 }
242
Jeff Brown4a06c802012-02-15 15:06:01 -0800243 synchronized (mLock) {
Jeff Brown43ea54b2012-03-09 14:37:48 -0800244 mAnimationCallbackQueue.removeCallbacksLocked(action, token);
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800245 if (action != null && token == null) {
246 mHandler.removeMessages(MSG_DO_SCHEDULE_ANIMATION, action);
247 }
Jeff Brown4a06c802012-02-15 15:06:01 -0800248 }
249 }
250
251 /**
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800252 * Posts a callback to run on the next draw cycle.
Jeff Brown4a06c802012-02-15 15:06:01 -0800253 * The callback only runs once and then is automatically removed.
254 *
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800255 * @param action The callback action to run during the next draw cycle.
256 * @param token The callback token, or null if none.
Jeff Brown4a06c802012-02-15 15:06:01 -0800257 *
258 * @see #removeDrawCallback
259 */
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800260 public void postDrawCallback(Runnable action, Object token) {
261 postDrawCallbackDelayed(action, token, 0);
Jeff Brown4a06c802012-02-15 15:06:01 -0800262 }
263
264 /**
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800265 * Posts a callback to run on the next draw cycle following the specified delay.
266 * The callback only runs once and then is automatically removed.
267 *
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800268 * @param action The callback action to run during the next animation cycle after
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800269 * the specified delay.
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800270 * @param token The callback token, or null if none.
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800271 * @param delayMillis The delay time in milliseconds.
272 *
273 * @see #removeDrawCallback
274 */
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800275 public void postDrawCallbackDelayed(Runnable action, Object token, long delayMillis) {
276 if (action == null) {
277 throw new IllegalArgumentException("action must not be null");
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800278 }
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800279
Jeff Brown43ea54b2012-03-09 14:37:48 -0800280 if (DEBUG) {
281 Log.d(TAG, "PostDrawCallback: " + action + ", token=" + token
282 + ", delayMillis=" + delayMillis);
283 }
284
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800285 synchronized (mLock) {
286 final long now = SystemClock.uptimeMillis();
287 final long dueTime = now + delayMillis;
Jeff Brown43ea54b2012-03-09 14:37:48 -0800288 mDrawCallbackQueue.addCallbackLocked(dueTime, action, token);
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800289 scheduleDrawLocked(now);
290
291 if (dueTime <= now) {
292 scheduleDrawLocked(now);
293 } else {
294 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_DRAW, action);
295 mHandler.sendMessageAtTime(msg, dueTime);
296 }
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800297 }
298 }
299
300 /**
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800301 * Removes draw callbacks that have the specified action and token.
Jeff Brown4a06c802012-02-15 15:06:01 -0800302 *
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800303 * @param action The action property of the callbacks to remove, or null to remove
304 * callbacks with any action.
305 * @param token The token property of the callbacks to remove, or null to remove
306 * callbacks with any token.
Jeff Brown4a06c802012-02-15 15:06:01 -0800307 *
308 * @see #postDrawCallback
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800309 * @see #postDrawCallbackDelayed
Jeff Brown4a06c802012-02-15 15:06:01 -0800310 */
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800311 public void removeDrawCallbacks(Runnable action, Object token) {
Jeff Brown43ea54b2012-03-09 14:37:48 -0800312 if (DEBUG) {
313 Log.d(TAG, "RemoveDrawCallbacks: " + action + ", token=" + token);
314 }
315
Jeff Brown4a06c802012-02-15 15:06:01 -0800316 synchronized (mLock) {
Jeff Brown43ea54b2012-03-09 14:37:48 -0800317 mDrawCallbackQueue.removeCallbacksLocked(action, token);
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800318 if (action != null && token == null) {
319 mHandler.removeMessages(MSG_DO_SCHEDULE_DRAW, action);
320 }
Jeff Brown4a06c802012-02-15 15:06:01 -0800321 }
322 }
323
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800324 private void scheduleAnimationLocked(long now) {
Jeff Brown4a06c802012-02-15 15:06:01 -0800325 if (!mAnimationScheduled) {
Jeff Brown96e942d2011-11-30 19:55:01 -0800326 mAnimationScheduled = true;
327 if (USE_VSYNC) {
328 if (DEBUG) {
329 Log.d(TAG, "Scheduling vsync for animation.");
330 }
Jeff Brown58aedbc2012-02-13 20:15:01 -0800331
332 // If running on the Looper thread, then schedule the vsync immediately,
333 // otherwise post a message to schedule the vsync from the UI thread
334 // as soon as possible.
Jeff Brown58aedbc2012-02-13 20:15:01 -0800335 if (isRunningOnLooperThreadLocked()) {
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800336 scheduleVsyncLocked();
Jeff Brown58aedbc2012-02-13 20:15:01 -0800337 } else {
Jeff Browne0dbd002012-02-15 19:34:58 -0800338 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
339 msg.setAsynchronous(true);
340 mHandler.sendMessageAtFrontOfQueue(msg);
Jeff Brown58aedbc2012-02-13 20:15:01 -0800341 }
Jeff Brown96e942d2011-11-30 19:55:01 -0800342 } else {
Jeff Brown96e942d2011-11-30 19:55:01 -0800343 final long nextAnimationTime = Math.max(mLastAnimationTime + sFrameDelay, now);
344 if (DEBUG) {
345 Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms.");
346 }
Jeff Browne0dbd002012-02-15 19:34:58 -0800347 Message msg = mHandler.obtainMessage(MSG_DO_ANIMATION);
348 msg.setAsynchronous(true);
349 mHandler.sendMessageAtTime(msg, nextAnimationTime);
Jeff Brown96e942d2011-11-30 19:55:01 -0800350 }
351 }
352 }
353
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800354 private void scheduleDrawLocked(long now) {
Jeff Brown4a06c802012-02-15 15:06:01 -0800355 if (!mDrawScheduled) {
Jeff Brown96858852012-02-14 13:45:06 -0800356 mDrawScheduled = true;
357 if (USE_ANIMATION_TIMER_FOR_DRAW) {
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800358 scheduleAnimationLocked(now);
Jeff Brown96858852012-02-14 13:45:06 -0800359 } else {
360 if (DEBUG) {
361 Log.d(TAG, "Scheduling draw immediately.");
Jeff Brown96e942d2011-11-30 19:55:01 -0800362 }
Jeff Browne0dbd002012-02-15 19:34:58 -0800363 Message msg = mHandler.obtainMessage(MSG_DO_DRAW);
364 msg.setAsynchronous(true);
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800365 mHandler.sendMessageAtTime(msg, now);
Jeff Brown96e942d2011-11-30 19:55:01 -0800366 }
367 }
368 }
369
Jeff Brown96858852012-02-14 13:45:06 -0800370 void doAnimation() {
371 doAnimationInner();
372
373 if (USE_ANIMATION_TIMER_FOR_DRAW) {
374 doDraw();
375 }
376 }
377
378 void doAnimationInner() {
379 final long start;
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800380 Callback callbacks;
Jeff Brown96858852012-02-14 13:45:06 -0800381 synchronized (mLock) {
382 if (!mAnimationScheduled) {
383 return; // no work to do
384 }
385 mAnimationScheduled = false;
386
387 start = SystemClock.uptimeMillis();
388 if (DEBUG) {
389 Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime)
390 + " ms have elapsed since previous animation.");
391 }
392 mLastAnimationTime = start;
393
Jeff Brown43ea54b2012-03-09 14:37:48 -0800394 callbacks = mAnimationCallbackQueue.extractDueCallbacksLocked(start);
Jeff Brown96858852012-02-14 13:45:06 -0800395 }
396
397 if (callbacks != null) {
398 runCallbacks(callbacks);
399 synchronized (mLock) {
400 recycleCallbacksLocked(callbacks);
401 }
402 }
403
404 if (DEBUG) {
405 Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms.");
406 }
407 }
408
409 void doDraw() {
410 final long start;
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800411 Callback callbacks;
Jeff Brown96858852012-02-14 13:45:06 -0800412 synchronized (mLock) {
413 if (!mDrawScheduled) {
414 return; // no work to do
415 }
416 mDrawScheduled = false;
417
418 start = SystemClock.uptimeMillis();
419 if (DEBUG) {
420 Log.d(TAG, "Performing draw: " + Math.max(0, start - mLastDrawTime)
421 + " ms have elapsed since previous draw.");
422 }
423 mLastDrawTime = start;
424
Jeff Brown43ea54b2012-03-09 14:37:48 -0800425 callbacks = mDrawCallbackQueue.extractDueCallbacksLocked(start);
Jeff Brown96858852012-02-14 13:45:06 -0800426 }
427
428 if (callbacks != null) {
429 runCallbacks(callbacks);
430 synchronized (mLock) {
431 recycleCallbacksLocked(callbacks);
432 }
433 }
434
435 if (DEBUG) {
436 Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms.");
437 }
438 }
439
440 void doScheduleVsync() {
441 synchronized (mLock) {
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800442 if (mAnimationScheduled) {
443 scheduleVsyncLocked();
444 }
Jeff Brown96858852012-02-14 13:45:06 -0800445 }
446 }
447
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800448 void doScheduleAnimation() {
449 synchronized (mLock) {
450 final long now = SystemClock.uptimeMillis();
Jeff Brown43ea54b2012-03-09 14:37:48 -0800451 if (mAnimationCallbackQueue.hasDueCallbacksLocked(now)) {
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800452 scheduleAnimationLocked(now);
453 }
Jeff Brown96e942d2011-11-30 19:55:01 -0800454 }
455 }
456
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800457 void doScheduleDraw() {
458 synchronized (mLock) {
459 final long now = SystemClock.uptimeMillis();
Jeff Brown43ea54b2012-03-09 14:37:48 -0800460 if (mDrawCallbackQueue.hasDueCallbacksLocked(now)) {
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800461 scheduleDrawLocked(now);
462 }
463 }
464 }
465
466 private void scheduleVsyncLocked() {
467 mDisplayEventReceiver.scheduleVsync();
468 }
469
Jeff Brown58aedbc2012-02-13 20:15:01 -0800470 private boolean isRunningOnLooperThreadLocked() {
471 return Looper.myLooper() == mLooper;
472 }
473
Jeff Brown96858852012-02-14 13:45:06 -0800474 private void runCallbacks(Callback head) {
475 while (head != null) {
Jeff Brown43ea54b2012-03-09 14:37:48 -0800476 if (DEBUG) {
477 Log.d(TAG, "RunCallback: " + head.action + ", token=" + head.token
478 + ", waitMillis=" + (SystemClock.uptimeMillis() - head.dueTime));
479 }
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800480 head.action.run();
Jeff Brown96858852012-02-14 13:45:06 -0800481 head = head.next;
482 }
483 }
484
485 private void recycleCallbacksLocked(Callback head) {
486 while (head != null) {
487 final Callback next = head.next;
488 recycleCallbackLocked(head);
489 head = next;
490 }
491 }
492
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800493 private Callback obtainCallbackLocked(long dueTime, Runnable action, Object token) {
Jeff Brown96858852012-02-14 13:45:06 -0800494 Callback callback = mCallbackPool;
495 if (callback == null) {
496 callback = new Callback();
497 } else {
498 mCallbackPool = callback.next;
499 callback.next = null;
500 }
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800501 callback.dueTime = dueTime;
502 callback.action = action;
503 callback.token = token;
Jeff Brown96858852012-02-14 13:45:06 -0800504 return callback;
505 }
506
507 private void recycleCallbackLocked(Callback callback) {
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800508 callback.action = null;
509 callback.token = null;
Jeff Brown96858852012-02-14 13:45:06 -0800510 callback.next = mCallbackPool;
511 mCallbackPool = callback;
512 }
513
Jeff Brown96858852012-02-14 13:45:06 -0800514 private final class FrameHandler extends Handler {
515 public FrameHandler(Looper looper) {
516 super(looper);
517 }
518
519 @Override
520 public void handleMessage(Message msg) {
521 switch (msg.what) {
522 case MSG_DO_ANIMATION:
523 doAnimation();
524 break;
525 case MSG_DO_DRAW:
526 doDraw();
527 break;
528 case MSG_DO_SCHEDULE_VSYNC:
529 doScheduleVsync();
530 break;
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800531 case MSG_DO_SCHEDULE_ANIMATION:
532 doScheduleAnimation();
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800533 break;
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800534 case MSG_DO_SCHEDULE_DRAW:
535 doScheduleDraw();
Jeff Brown2b6cb9a2012-03-05 17:21:01 -0800536 break;
Jeff Brown96858852012-02-14 13:45:06 -0800537 }
538 }
539 }
540
Jeff Brown96e942d2011-11-30 19:55:01 -0800541 private final class FrameDisplayEventReceiver extends DisplayEventReceiver {
542 public FrameDisplayEventReceiver(Looper looper) {
543 super(looper);
544 }
545
546 @Override
547 public void onVsync(long timestampNanos, int frame) {
548 doAnimation();
549 }
550 }
Jeff Brown96858852012-02-14 13:45:06 -0800551
552 private static final class Callback {
553 public Callback next;
Jeff Brown7ae9d5f2012-03-05 19:33:49 -0800554 public long dueTime;
555 public Runnable action;
556 public Object token;
Jeff Brown96858852012-02-14 13:45:06 -0800557 }
Jeff Brown43ea54b2012-03-09 14:37:48 -0800558
559 private final class CallbackQueue {
560 private Callback mHead;
561
562 public boolean hasDueCallbacksLocked(long now) {
563 return mHead != null && mHead.dueTime <= now;
564 }
565
566 public Callback extractDueCallbacksLocked(long now) {
567 Callback callbacks = mHead;
568 if (callbacks == null || callbacks.dueTime > now) {
569 return null;
570 }
571
572 Callback last = callbacks;
573 Callback next = last.next;
574 while (next != null) {
575 if (next.dueTime > now) {
576 last.next = null;
577 break;
578 }
579 last = next;
580 next = next.next;
581 }
582 mHead = next;
583 return callbacks;
584 }
585
586 public void addCallbackLocked(long dueTime, Runnable action, Object token) {
587 Callback callback = obtainCallbackLocked(dueTime, action, token);
588 Callback entry = mHead;
589 if (entry == null) {
590 mHead = callback;
591 return;
592 }
593 if (dueTime < entry.dueTime) {
594 callback.next = entry;
595 mHead = callback;
596 return;
597 }
598 while (entry.next != null) {
599 if (dueTime < entry.next.dueTime) {
600 callback.next = entry.next;
601 break;
602 }
603 entry = entry.next;
604 }
605 entry.next = callback;
606 }
607
608 public void removeCallbacksLocked(Runnable action, Object token) {
609 Callback predecessor = null;
610 for (Callback callback = mHead; callback != null;) {
611 final Callback next = callback.next;
612 if ((action == null || callback.action == action)
613 && (token == null || callback.token == token)) {
614 if (predecessor != null) {
615 predecessor.next = next;
616 } else {
617 mHead = next;
618 }
619 recycleCallbackLocked(callback);
620 } else {
621 predecessor = callback;
622 }
623 callback = next;
624 }
625 }
626 }
Jeff Brown96e942d2011-11-30 19:55:01 -0800627}