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