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