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