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