1 /* 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 17 package android.view; 18 19 import android.hardware.display.DisplayManagerGlobal; 20 import android.os.Handler; 21 import android.os.Looper; 22 import android.os.Message; 23 import android.os.SystemClock; 24 import android.os.SystemProperties; 25 import android.util.Log; 26 import android.util.TimeUtils; 27 28 import java.io.PrintWriter; 29 30 /** 31 * Coordinates the timing of animations, input and drawing. 32 * <p> 33 * The choreographer receives timing pulses (such as vertical synchronization) 34 * from the display subsystem then schedules work to occur as part of rendering 35 * the next display frame. 36 * </p><p> 37 * Applications typically interact with the choreographer indirectly using 38 * higher level abstractions in the animation framework or the view hierarchy. 39 * Here are some examples of things you can do using the higher-level APIs. 40 * </p> 41 * <ul> 42 * <li>To post an animation to be processed on a regular time basis synchronized with 43 * display frame rendering, use {@link android.animation.ValueAnimator#start}.</li> 44 * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display 45 * frame, use {@link View#postOnAnimation}.</li> 46 * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display 47 * frame after a delay, use {@link View#postOnAnimationDelayed}.</li> 48 * <li>To post a call to {@link View#invalidate()} to occur once at the beginning of the 49 * next display frame, use {@link View#postInvalidateOnAnimation()} or 50 * {@link View#postInvalidateOnAnimation(int, int, int, int)}.</li> 51 * <li>To ensure that the contents of a {@link View} scroll smoothly and are drawn in 52 * sync with display frame rendering, do nothing. This already happens automatically. 53 * {@link View#onDraw} will be called at the appropriate time.</li> 54 * </ul> 55 * <p> 56 * However, there are a few cases where you might want to use the functions of the 57 * choreographer directly in your application. Here are some examples. 58 * </p> 59 * <ul> 60 * <li>If your application does its rendering in a different thread, possibly using GL, 61 * or does not use the animation framework or view hierarchy at all 62 * and you want to ensure that it is appropriately synchronized with the display, then use 63 * {@link Choreographer#postFrameCallback}.</li> 64 * <li>... and that's about it.</li> 65 * </ul> 66 * <p> 67 * Each {@link Looper} thread has its own choreographer. Other threads can 68 * post callbacks to run on the choreographer but they will run on the {@link Looper} 69 * to which the choreographer belongs. 70 * </p> 71 */ 72 public final class Choreographer { 73 private static final String TAG = "Choreographer"; 74 private static final boolean DEBUG = false; 75 76 // The default amount of time in ms between animation frames. 77 // When vsync is not enabled, we want to have some idea of how long we should 78 // wait before posting the next animation message. It is important that the 79 // default value be less than the true inter-frame delay on all devices to avoid 80 // situations where we might skip frames by waiting too long (we must compensate 81 // for jitter and hardware variations). Regardless of this value, the animation 82 // and display loop is ultimately rate-limited by how fast new graphics buffers can 83 // be dequeued. 84 private static final long DEFAULT_FRAME_DELAY = 10; 85 86 // The number of milliseconds between animation frames. 87 private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY; 88 89 // Thread local storage for the choreographer. 90 private static final ThreadLocal<Choreographer> sThreadInstance = 91 new ThreadLocal<Choreographer>() { 92 @Override 93 protected Choreographer initialValue() { 94 Looper looper = Looper.myLooper(); 95 if (looper == null) { 96 throw new IllegalStateException("The current thread must have a looper!"); 97 } 98 return new Choreographer(looper); 99 } 100 }; 101 102 // Enable/disable vsync for animations and drawing. 103 private static final boolean USE_VSYNC = SystemProperties.getBoolean( 104 "debug.choreographer.vsync", true); 105 106 // Enable/disable using the frame time instead of returning now. 107 private static final boolean USE_FRAME_TIME = SystemProperties.getBoolean( 108 "debug.choreographer.frametime", true); 109 110 // Set a limit to warn about skipped frames. 111 // Skipped frames imply jank. 112 private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt( 113 "debug.choreographer.skipwarning", 30); 114 115 private static final int MSG_DO_FRAME = 0; 116 private static final int MSG_DO_SCHEDULE_VSYNC = 1; 117 private static final int MSG_DO_SCHEDULE_CALLBACK = 2; 118 119 // All frame callbacks posted by applications have this token. 120 private static final Object FRAME_CALLBACK_TOKEN = new Object() { 121 public String toString() { return "FRAME_CALLBACK_TOKEN"; } 122 }; 123 124 private final Object mLock = new Object(); 125 126 private final Looper mLooper; 127 private final FrameHandler mHandler; 128 129 // The display event receiver can only be accessed by the looper thread to which 130 // it is attached. We take care to ensure that we post message to the looper 131 // if appropriate when interacting with the display event receiver. 132 private final FrameDisplayEventReceiver mDisplayEventReceiver; 133 134 private CallbackRecord mCallbackPool; 135 136 private final CallbackQueue[] mCallbackQueues; 137 138 private boolean mFrameScheduled; 139 private boolean mCallbacksRunning; 140 private long mLastFrameTimeNanos; 141 private long mFrameIntervalNanos; 142 143 /** 144 * Callback type: Input callback. Runs first. 145 * @hide 146 */ 147 public static final int CALLBACK_INPUT = 0; 148 149 /** 150 * Callback type: Animation callback. Runs before traversals. 151 * @hide 152 */ 153 public static final int CALLBACK_ANIMATION = 1; 154 155 /** 156 * Callback type: Traversal callback. Handles layout and draw. Runs last 157 * after all other asynchronous messages have been handled. 158 * @hide 159 */ 160 public static final int CALLBACK_TRAVERSAL = 2; 161 162 private static final int CALLBACK_LAST = CALLBACK_TRAVERSAL; 163 Choreographer(Looper looper)164 private Choreographer(Looper looper) { 165 mLooper = looper; 166 mHandler = new FrameHandler(looper); 167 mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null; 168 mLastFrameTimeNanos = Long.MIN_VALUE; 169 170 mFrameIntervalNanos = (long)(1000000000 / getRefreshRate()); 171 172 mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; 173 for (int i = 0; i <= CALLBACK_LAST; i++) { 174 mCallbackQueues[i] = new CallbackQueue(); 175 } 176 } 177 getRefreshRate()178 private static float getRefreshRate() { 179 DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo( 180 Display.DEFAULT_DISPLAY); 181 return di.refreshRate; 182 } 183 184 /** 185 * Gets the choreographer for the calling thread. Must be called from 186 * a thread that already has a {@link android.os.Looper} associated with it. 187 * 188 * @return The choreographer for this thread. 189 * @throws IllegalStateException if the thread does not have a looper. 190 */ getInstance()191 public static Choreographer getInstance() { 192 return sThreadInstance.get(); 193 } 194 195 /** 196 * The amount of time, in milliseconds, between each frame of the animation. 197 * <p> 198 * This is a requested time that the animation will attempt to honor, but the actual delay 199 * between frames may be different, depending on system load and capabilities. This is a static 200 * function because the same delay will be applied to all animations, since they are all 201 * run off of a single timing loop. 202 * </p><p> 203 * The frame delay may be ignored when the animation system uses an external timing 204 * source, such as the display refresh rate (vsync), to govern animations. 205 * </p> 206 * 207 * @return the requested time between frames, in milliseconds 208 * @hide 209 */ getFrameDelay()210 public static long getFrameDelay() { 211 return sFrameDelay; 212 } 213 214 /** 215 * The amount of time, in milliseconds, between each frame of the animation. 216 * <p> 217 * This is a requested time that the animation will attempt to honor, but the actual delay 218 * between frames may be different, depending on system load and capabilities. This is a static 219 * function because the same delay will be applied to all animations, since they are all 220 * run off of a single timing loop. 221 * </p><p> 222 * The frame delay may be ignored when the animation system uses an external timing 223 * source, such as the display refresh rate (vsync), to govern animations. 224 * </p> 225 * 226 * @param frameDelay the requested time between frames, in milliseconds 227 * @hide 228 */ setFrameDelay(long frameDelay)229 public static void setFrameDelay(long frameDelay) { 230 sFrameDelay = frameDelay; 231 } 232 233 /** 234 * Subtracts typical frame delay time from a delay interval in milliseconds. 235 * <p> 236 * This method can be used to compensate for animation delay times that have baked 237 * in assumptions about the frame delay. For example, it's quite common for code to 238 * assume a 60Hz frame time and bake in a 16ms delay. When we call 239 * {@link #postAnimationCallbackDelayed} we want to know how long to wait before 240 * posting the animation callback but let the animation timer take care of the remaining 241 * frame delay time. 242 * </p><p> 243 * This method is somewhat conservative about how much of the frame delay it 244 * subtracts. It uses the same value returned by {@link #getFrameDelay} which by 245 * default is 10ms even though many parts of the system assume 16ms. Consequently, 246 * we might still wait 6ms before posting an animation callback that we want to run 247 * on the next frame, but this is much better than waiting a whole 16ms and likely 248 * missing the deadline. 249 * </p> 250 * 251 * @param delayMillis The original delay time including an assumed frame delay. 252 * @return The adjusted delay time with the assumed frame delay subtracted out. 253 * @hide 254 */ subtractFrameDelay(long delayMillis)255 public static long subtractFrameDelay(long delayMillis) { 256 final long frameDelay = sFrameDelay; 257 return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay; 258 } 259 260 /** 261 * @return The refresh rate as the nanoseconds between frames 262 * @hide 263 */ getFrameIntervalNanos()264 public long getFrameIntervalNanos() { 265 return mFrameIntervalNanos; 266 } 267 dump(String prefix, PrintWriter writer)268 void dump(String prefix, PrintWriter writer) { 269 String innerPrefix = prefix + " "; 270 writer.print(prefix); writer.println("Choreographer:"); 271 writer.print(innerPrefix); writer.print("mFrameScheduled="); 272 writer.println(mFrameScheduled); 273 writer.print(innerPrefix); writer.print("mLastFrameTime="); 274 writer.println(TimeUtils.formatUptime(mLastFrameTimeNanos / 1000000)); 275 } 276 277 /** 278 * Posts a callback to run on the next frame. 279 * <p> 280 * The callback runs once then is automatically removed. 281 * </p> 282 * 283 * @param callbackType The callback type. 284 * @param action The callback action to run during the next frame. 285 * @param token The callback token, or null if none. 286 * 287 * @see #removeCallbacks 288 * @hide 289 */ postCallback(int callbackType, Runnable action, Object token)290 public void postCallback(int callbackType, Runnable action, Object token) { 291 postCallbackDelayed(callbackType, action, token, 0); 292 } 293 294 /** 295 * Posts a callback to run on the next frame after the specified delay. 296 * <p> 297 * The callback runs once then is automatically removed. 298 * </p> 299 * 300 * @param callbackType The callback type. 301 * @param action The callback action to run during the next frame after the specified delay. 302 * @param token The callback token, or null if none. 303 * @param delayMillis The delay time in milliseconds. 304 * 305 * @see #removeCallback 306 * @hide 307 */ postCallbackDelayed(int callbackType, Runnable action, Object token, long delayMillis)308 public void postCallbackDelayed(int callbackType, 309 Runnable action, Object token, long delayMillis) { 310 if (action == null) { 311 throw new IllegalArgumentException("action must not be null"); 312 } 313 if (callbackType < 0 || callbackType > CALLBACK_LAST) { 314 throw new IllegalArgumentException("callbackType is invalid"); 315 } 316 317 postCallbackDelayedInternal(callbackType, action, token, delayMillis); 318 } 319 postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis)320 private void postCallbackDelayedInternal(int callbackType, 321 Object action, Object token, long delayMillis) { 322 if (DEBUG) { 323 Log.d(TAG, "PostCallback: type=" + callbackType 324 + ", action=" + action + ", token=" + token 325 + ", delayMillis=" + delayMillis); 326 } 327 328 synchronized (mLock) { 329 final long now = SystemClock.uptimeMillis(); 330 final long dueTime = now + delayMillis; 331 mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); 332 333 if (dueTime <= now) { 334 scheduleFrameLocked(now); 335 } else { 336 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); 337 msg.arg1 = callbackType; 338 msg.setAsynchronous(true); 339 mHandler.sendMessageAtTime(msg, dueTime); 340 } 341 } 342 } 343 344 /** 345 * Removes callbacks that have the specified action and token. 346 * 347 * @param callbackType The callback type. 348 * @param action The action property of the callbacks to remove, or null to remove 349 * callbacks with any action. 350 * @param token The token property of the callbacks to remove, or null to remove 351 * callbacks with any token. 352 * 353 * @see #postCallback 354 * @see #postCallbackDelayed 355 * @hide 356 */ removeCallbacks(int callbackType, Runnable action, Object token)357 public void removeCallbacks(int callbackType, Runnable action, Object token) { 358 if (callbackType < 0 || callbackType > CALLBACK_LAST) { 359 throw new IllegalArgumentException("callbackType is invalid"); 360 } 361 362 removeCallbacksInternal(callbackType, action, token); 363 } 364 removeCallbacksInternal(int callbackType, Object action, Object token)365 private void removeCallbacksInternal(int callbackType, Object action, Object token) { 366 if (DEBUG) { 367 Log.d(TAG, "RemoveCallbacks: type=" + callbackType 368 + ", action=" + action + ", token=" + token); 369 } 370 371 synchronized (mLock) { 372 mCallbackQueues[callbackType].removeCallbacksLocked(action, token); 373 if (action != null && token == null) { 374 mHandler.removeMessages(MSG_DO_SCHEDULE_CALLBACK, action); 375 } 376 } 377 } 378 379 /** 380 * Posts a frame callback to run on the next frame. 381 * <p> 382 * The callback runs once then is automatically removed. 383 * </p> 384 * 385 * @param callback The frame callback to run during the next frame. 386 * 387 * @see #postFrameCallbackDelayed 388 * @see #removeFrameCallback 389 */ postFrameCallback(FrameCallback callback)390 public void postFrameCallback(FrameCallback callback) { 391 postFrameCallbackDelayed(callback, 0); 392 } 393 394 /** 395 * Posts a frame callback to run on the next frame after the specified delay. 396 * <p> 397 * The callback runs once then is automatically removed. 398 * </p> 399 * 400 * @param callback The frame callback to run during the next frame. 401 * @param delayMillis The delay time in milliseconds. 402 * 403 * @see #postFrameCallback 404 * @see #removeFrameCallback 405 */ postFrameCallbackDelayed(FrameCallback callback, long delayMillis)406 public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) { 407 if (callback == null) { 408 throw new IllegalArgumentException("callback must not be null"); 409 } 410 411 postCallbackDelayedInternal(CALLBACK_ANIMATION, 412 callback, FRAME_CALLBACK_TOKEN, delayMillis); 413 } 414 415 /** 416 * Removes a previously posted frame callback. 417 * 418 * @param callback The frame callback to remove. 419 * 420 * @see #postFrameCallback 421 * @see #postFrameCallbackDelayed 422 */ removeFrameCallback(FrameCallback callback)423 public void removeFrameCallback(FrameCallback callback) { 424 if (callback == null) { 425 throw new IllegalArgumentException("callback must not be null"); 426 } 427 428 removeCallbacksInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN); 429 } 430 431 /** 432 * Gets the time when the current frame started. 433 * <p> 434 * This method provides the time in milliseconds when the frame started being rendered. 435 * The frame time provides a stable time base for synchronizing animations 436 * and drawing. It should be used instead of {@link SystemClock#uptimeMillis()} 437 * or {@link System#nanoTime()} for animations and drawing in the UI. Using the frame 438 * time helps to reduce inter-frame jitter because the frame time is fixed at the time 439 * the frame was scheduled to start, regardless of when the animations or drawing 440 * callback actually runs. All callbacks that run as part of rendering a frame will 441 * observe the same frame time so using the frame time also helps to synchronize effects 442 * that are performed by different callbacks. 443 * </p><p> 444 * Please note that the framework already takes care to process animations and 445 * drawing using the frame time as a stable time base. Most applications should 446 * not need to use the frame time information directly. 447 * </p><p> 448 * This method should only be called from within a callback. 449 * </p> 450 * 451 * @return The frame start time, in the {@link SystemClock#uptimeMillis()} time base. 452 * 453 * @throws IllegalStateException if no frame is in progress. 454 * @hide 455 */ getFrameTime()456 public long getFrameTime() { 457 return getFrameTimeNanos() / TimeUtils.NANOS_PER_MS; 458 } 459 460 /** 461 * Same as {@link #getFrameTime()} but with nanosecond precision. 462 * 463 * @return The frame start time, in the {@link System#nanoTime()} time base. 464 * 465 * @throws IllegalStateException if no frame is in progress. 466 * @hide 467 */ getFrameTimeNanos()468 public long getFrameTimeNanos() { 469 synchronized (mLock) { 470 if (!mCallbacksRunning) { 471 throw new IllegalStateException("This method must only be called as " 472 + "part of a callback while a frame is in progress."); 473 } 474 return USE_FRAME_TIME ? mLastFrameTimeNanos : System.nanoTime(); 475 } 476 } 477 scheduleFrameLocked(long now)478 private void scheduleFrameLocked(long now) { 479 if (!mFrameScheduled) { 480 mFrameScheduled = true; 481 if (USE_VSYNC) { 482 if (DEBUG) { 483 Log.d(TAG, "Scheduling next frame on vsync."); 484 } 485 486 // If running on the Looper thread, then schedule the vsync immediately, 487 // otherwise post a message to schedule the vsync from the UI thread 488 // as soon as possible. 489 if (isRunningOnLooperThreadLocked()) { 490 scheduleVsyncLocked(); 491 } else { 492 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); 493 msg.setAsynchronous(true); 494 mHandler.sendMessageAtFrontOfQueue(msg); 495 } 496 } else { 497 final long nextFrameTime = Math.max( 498 mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now); 499 if (DEBUG) { 500 Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms."); 501 } 502 Message msg = mHandler.obtainMessage(MSG_DO_FRAME); 503 msg.setAsynchronous(true); 504 mHandler.sendMessageAtTime(msg, nextFrameTime); 505 } 506 } 507 } 508 doFrame(long frameTimeNanos, int frame)509 void doFrame(long frameTimeNanos, int frame) { 510 final long startNanos; 511 synchronized (mLock) { 512 if (!mFrameScheduled) { 513 return; // no work to do 514 } 515 516 startNanos = System.nanoTime(); 517 final long jitterNanos = startNanos - frameTimeNanos; 518 if (jitterNanos >= mFrameIntervalNanos) { 519 final long skippedFrames = jitterNanos / mFrameIntervalNanos; 520 if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) { 521 Log.i(TAG, "Skipped " + skippedFrames + " frames! " 522 + "The application may be doing too much work on its main thread."); 523 } 524 final long lastFrameOffset = jitterNanos % mFrameIntervalNanos; 525 if (DEBUG) { 526 Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms " 527 + "which is more than the frame interval of " 528 + (mFrameIntervalNanos * 0.000001f) + " ms! " 529 + "Skipping " + skippedFrames + " frames and setting frame " 530 + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past."); 531 } 532 frameTimeNanos = startNanos - lastFrameOffset; 533 } 534 535 if (frameTimeNanos < mLastFrameTimeNanos) { 536 if (DEBUG) { 537 Log.d(TAG, "Frame time appears to be going backwards. May be due to a " 538 + "previously skipped frame. Waiting for next vsync."); 539 } 540 scheduleVsyncLocked(); 541 return; 542 } 543 544 mFrameScheduled = false; 545 mLastFrameTimeNanos = frameTimeNanos; 546 } 547 548 doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); 549 doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); 550 doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); 551 552 if (DEBUG) { 553 final long endNanos = System.nanoTime(); 554 Log.d(TAG, "Frame " + frame + ": Finished, took " 555 + (endNanos - startNanos) * 0.000001f + " ms, latency " 556 + (startNanos - frameTimeNanos) * 0.000001f + " ms."); 557 } 558 } 559 doCallbacks(int callbackType, long frameTimeNanos)560 void doCallbacks(int callbackType, long frameTimeNanos) { 561 CallbackRecord callbacks; 562 synchronized (mLock) { 563 // We use "now" to determine when callbacks become due because it's possible 564 // for earlier processing phases in a frame to post callbacks that should run 565 // in a following phase, such as an input event that causes an animation to start. 566 final long now = SystemClock.uptimeMillis(); 567 callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now); 568 if (callbacks == null) { 569 return; 570 } 571 mCallbacksRunning = true; 572 } 573 try { 574 for (CallbackRecord c = callbacks; c != null; c = c.next) { 575 if (DEBUG) { 576 Log.d(TAG, "RunCallback: type=" + callbackType 577 + ", action=" + c.action + ", token=" + c.token 578 + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime)); 579 } 580 c.run(frameTimeNanos); 581 } 582 } finally { 583 synchronized (mLock) { 584 mCallbacksRunning = false; 585 do { 586 final CallbackRecord next = callbacks.next; 587 recycleCallbackLocked(callbacks); 588 callbacks = next; 589 } while (callbacks != null); 590 } 591 } 592 } 593 doScheduleVsync()594 void doScheduleVsync() { 595 synchronized (mLock) { 596 if (mFrameScheduled) { 597 scheduleVsyncLocked(); 598 } 599 } 600 } 601 doScheduleCallback(int callbackType)602 void doScheduleCallback(int callbackType) { 603 synchronized (mLock) { 604 if (!mFrameScheduled) { 605 final long now = SystemClock.uptimeMillis(); 606 if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) { 607 scheduleFrameLocked(now); 608 } 609 } 610 } 611 } 612 scheduleVsyncLocked()613 private void scheduleVsyncLocked() { 614 mDisplayEventReceiver.scheduleVsync(); 615 } 616 isRunningOnLooperThreadLocked()617 private boolean isRunningOnLooperThreadLocked() { 618 return Looper.myLooper() == mLooper; 619 } 620 obtainCallbackLocked(long dueTime, Object action, Object token)621 private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) { 622 CallbackRecord callback = mCallbackPool; 623 if (callback == null) { 624 callback = new CallbackRecord(); 625 } else { 626 mCallbackPool = callback.next; 627 callback.next = null; 628 } 629 callback.dueTime = dueTime; 630 callback.action = action; 631 callback.token = token; 632 return callback; 633 } 634 recycleCallbackLocked(CallbackRecord callback)635 private void recycleCallbackLocked(CallbackRecord callback) { 636 callback.action = null; 637 callback.token = null; 638 callback.next = mCallbackPool; 639 mCallbackPool = callback; 640 } 641 642 /** 643 * Implement this interface to receive a callback when a new display frame is 644 * being rendered. The callback is invoked on the {@link Looper} thread to 645 * which the {@link Choreographer} is attached. 646 */ 647 public interface FrameCallback { 648 /** 649 * Called when a new display frame is being rendered. 650 * <p> 651 * This method provides the time in nanoseconds when the frame started being rendered. 652 * The frame time provides a stable time base for synchronizing animations 653 * and drawing. It should be used instead of {@link SystemClock#uptimeMillis()} 654 * or {@link System#nanoTime()} for animations and drawing in the UI. Using the frame 655 * time helps to reduce inter-frame jitter because the frame time is fixed at the time 656 * the frame was scheduled to start, regardless of when the animations or drawing 657 * callback actually runs. All callbacks that run as part of rendering a frame will 658 * observe the same frame time so using the frame time also helps to synchronize effects 659 * that are performed by different callbacks. 660 * </p><p> 661 * Please note that the framework already takes care to process animations and 662 * drawing using the frame time as a stable time base. Most applications should 663 * not need to use the frame time information directly. 664 * </p> 665 * 666 * @param frameTimeNanos The time in nanoseconds when the frame started being rendered, 667 * in the {@link System#nanoTime()} timebase. Divide this value by {@code 1000000} 668 * to convert it to the {@link SystemClock#uptimeMillis()} time base. 669 */ doFrame(long frameTimeNanos)670 public void doFrame(long frameTimeNanos); 671 } 672 673 private final class FrameHandler extends Handler { FrameHandler(Looper looper)674 public FrameHandler(Looper looper) { 675 super(looper); 676 } 677 678 @Override handleMessage(Message msg)679 public void handleMessage(Message msg) { 680 switch (msg.what) { 681 case MSG_DO_FRAME: 682 doFrame(System.nanoTime(), 0); 683 break; 684 case MSG_DO_SCHEDULE_VSYNC: 685 doScheduleVsync(); 686 break; 687 case MSG_DO_SCHEDULE_CALLBACK: 688 doScheduleCallback(msg.arg1); 689 break; 690 } 691 } 692 } 693 694 private final class FrameDisplayEventReceiver extends DisplayEventReceiver 695 implements Runnable { 696 private boolean mHavePendingVsync; 697 private long mTimestampNanos; 698 private int mFrame; 699 FrameDisplayEventReceiver(Looper looper)700 public FrameDisplayEventReceiver(Looper looper) { 701 super(looper); 702 } 703 704 @Override onVsync(long timestampNanos, int builtInDisplayId, int frame)705 public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { 706 // Ignore vsync from secondary display. 707 // This can be problematic because the call to scheduleVsync() is a one-shot. 708 // We need to ensure that we will still receive the vsync from the primary 709 // display which is the one we really care about. Ideally we should schedule 710 // vsync for a particular display. 711 // At this time Surface Flinger won't send us vsyncs for secondary displays 712 // but that could change in the future so let's log a message to help us remember 713 // that we need to fix this. 714 if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { 715 Log.d(TAG, "Received vsync from secondary display, but we don't support " 716 + "this case yet. Choreographer needs a way to explicitly request " 717 + "vsync for a specific display to ensure it doesn't lose track " 718 + "of its scheduled vsync."); 719 scheduleVsync(); 720 return; 721 } 722 723 // Post the vsync event to the Handler. 724 // The idea is to prevent incoming vsync events from completely starving 725 // the message queue. If there are no messages in the queue with timestamps 726 // earlier than the frame time, then the vsync event will be processed immediately. 727 // Otherwise, messages that predate the vsync event will be handled first. 728 long now = System.nanoTime(); 729 if (timestampNanos > now) { 730 Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f) 731 + " ms in the future! Check that graphics HAL is generating vsync " 732 + "timestamps using the correct timebase."); 733 timestampNanos = now; 734 } 735 736 if (mHavePendingVsync) { 737 Log.w(TAG, "Already have a pending vsync event. There should only be " 738 + "one at a time."); 739 } else { 740 mHavePendingVsync = true; 741 } 742 743 mTimestampNanos = timestampNanos; 744 mFrame = frame; 745 Message msg = Message.obtain(mHandler, this); 746 msg.setAsynchronous(true); 747 mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); 748 } 749 750 @Override run()751 public void run() { 752 mHavePendingVsync = false; 753 doFrame(mTimestampNanos, mFrame); 754 } 755 } 756 757 private static final class CallbackRecord { 758 public CallbackRecord next; 759 public long dueTime; 760 public Object action; // Runnable or FrameCallback 761 public Object token; 762 run(long frameTimeNanos)763 public void run(long frameTimeNanos) { 764 if (token == FRAME_CALLBACK_TOKEN) { 765 ((FrameCallback)action).doFrame(frameTimeNanos); 766 } else { 767 ((Runnable)action).run(); 768 } 769 } 770 } 771 772 private final class CallbackQueue { 773 private CallbackRecord mHead; 774 hasDueCallbacksLocked(long now)775 public boolean hasDueCallbacksLocked(long now) { 776 return mHead != null && mHead.dueTime <= now; 777 } 778 extractDueCallbacksLocked(long now)779 public CallbackRecord extractDueCallbacksLocked(long now) { 780 CallbackRecord callbacks = mHead; 781 if (callbacks == null || callbacks.dueTime > now) { 782 return null; 783 } 784 785 CallbackRecord last = callbacks; 786 CallbackRecord next = last.next; 787 while (next != null) { 788 if (next.dueTime > now) { 789 last.next = null; 790 break; 791 } 792 last = next; 793 next = next.next; 794 } 795 mHead = next; 796 return callbacks; 797 } 798 addCallbackLocked(long dueTime, Object action, Object token)799 public void addCallbackLocked(long dueTime, Object action, Object token) { 800 CallbackRecord callback = obtainCallbackLocked(dueTime, action, token); 801 CallbackRecord entry = mHead; 802 if (entry == null) { 803 mHead = callback; 804 return; 805 } 806 if (dueTime < entry.dueTime) { 807 callback.next = entry; 808 mHead = callback; 809 return; 810 } 811 while (entry.next != null) { 812 if (dueTime < entry.next.dueTime) { 813 callback.next = entry.next; 814 break; 815 } 816 entry = entry.next; 817 } 818 entry.next = callback; 819 } 820 removeCallbacksLocked(Object action, Object token)821 public void removeCallbacksLocked(Object action, Object token) { 822 CallbackRecord predecessor = null; 823 for (CallbackRecord callback = mHead; callback != null;) { 824 final CallbackRecord next = callback.next; 825 if ((action == null || callback.action == action) 826 && (token == null || callback.token == token)) { 827 if (predecessor != null) { 828 predecessor.next = next; 829 } else { 830 mHead = next; 831 } 832 recycleCallbackLocked(callback); 833 } else { 834 predecessor = callback; 835 } 836 callback = next; 837 } 838 } 839 } 840 } 841