Loading core/java/android/view/Choreographer.java +88 −50 Original line number Diff line number Diff line Loading @@ -27,6 +27,10 @@ import android.util.Log; /** * Coordinates animations and drawing for UI on a particular thread. * * This object is thread-safe. Other threads can add and remove listeners * or schedule work to occur at a later time on the UI thread. * * @hide */ public final class Choreographer extends Handler { Loading @@ -44,7 +48,7 @@ public final class Choreographer extends Handler { private static final long DEFAULT_FRAME_DELAY = 10; // The number of milliseconds between animation frames. private static long sFrameDelay = DEFAULT_FRAME_DELAY; private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY; // Thread local storage for the choreographer. private static final ThreadLocal<Choreographer> sThreadInstance = Loading Loading @@ -75,6 +79,8 @@ public final class Choreographer extends Handler { private static final int MSG_DO_ANIMATION = 0; private static final int MSG_DO_DRAW = 1; private final Object mLock = new Object(); private final Looper mLooper; private OnAnimateListener[] mOnAnimateListeners; Loading Loading @@ -138,9 +144,14 @@ public final class Choreographer extends Handler { /** * Schedules animation (and drawing) to occur on the next frame synchronization boundary. * Must be called on the UI thread. */ public void scheduleAnimation() { synchronized (mLock) { scheduleAnimationLocked(); } } private void scheduleAnimationLocked() { if (!mAnimationScheduled) { mAnimationScheduled = true; if (USE_VSYNC) { Loading @@ -163,23 +174,26 @@ public final class Choreographer extends Handler { } /** * Return true if {@link #scheduleAnimation()} has been called but * Returns true if {@link #scheduleAnimation()} has been called but * {@link OnAnimateListener#onAnimate() OnAnimateListener.onAnimate()} has * not yet been called. */ public boolean isAnimationScheduled() { synchronized (mLock) { return mAnimationScheduled; } } /** * Schedules drawing to occur on the next frame synchronization boundary. * Must be called on the UI thread. */ public void scheduleDraw() { synchronized (mLock) { if (!mDrawScheduled) { mDrawScheduled = true; if (USE_ANIMATION_TIMER_FOR_DRAW) { scheduleAnimation(); scheduleAnimationLocked(); } else { if (DEBUG) { Log.d(TAG, "Scheduling draw immediately."); Loading @@ -188,15 +202,18 @@ public final class Choreographer extends Handler { } } } } /** * Return true if {@link #scheduleDraw()} has been called but * Returns true if {@link #scheduleDraw()} has been called but * {@link OnDrawListener#onDraw() OnDrawListener.onDraw()} has * not yet been called. */ public boolean isDrawScheduled() { synchronized (mLock) { return mDrawScheduled; } } @Override public void handleMessage(Message msg) { Loading @@ -211,17 +228,32 @@ public final class Choreographer extends Handler { } private void doAnimation() { if (mAnimationScheduled) { doAnimationInner(); if (USE_ANIMATION_TIMER_FOR_DRAW) { doDraw(); } } private void doAnimationInner() { final long start; final OnAnimateListener[] listeners; synchronized (mLock) { if (!mAnimationScheduled) { return; // no work to do } mAnimationScheduled = false; final long start = SystemClock.uptimeMillis(); start = SystemClock.uptimeMillis(); if (DEBUG) { Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime) + " ms have elapsed since previous animation."); } mLastAnimationTime = start; final OnAnimateListener[] listeners = mOnAnimateListeners; listeners = mOnAnimateListeners; } if (listeners != null) { for (int i = 0; i < listeners.length; i++) { listeners[i].onAnimate(); Loading @@ -233,23 +265,25 @@ public final class Choreographer extends Handler { } } if (USE_ANIMATION_TIMER_FOR_DRAW) { doDraw(); } } private void doDraw() { if (mDrawScheduled) { final long start; final OnDrawListener[] listeners; synchronized (mLock) { if (!mDrawScheduled) { return; // no work to do } mDrawScheduled = false; final long start = SystemClock.uptimeMillis(); start = SystemClock.uptimeMillis(); if (DEBUG) { Log.d(TAG, "Performing draw: " + Math.max(0, start - mLastDrawTime) + " ms have elapsed since previous draw."); } mLastDrawTime = start; final OnDrawListener[] listeners = mOnDrawListeners; listeners = mOnDrawListeners; } if (listeners != null) { for (int i = 0; i < listeners.length; i++) { listeners[i].onDraw(); Loading @@ -260,11 +294,9 @@ public final class Choreographer extends Handler { Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms."); } } } /** * Adds an animation listener. * Must be called on the UI thread. * * @param listener The listener to add. */ Loading @@ -277,13 +309,14 @@ public final class Choreographer extends Handler { Log.d(TAG, "Adding onAnimate listener: " + listener); } synchronized (mLock) { mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class, mOnAnimateListeners, listener); } } /** * Removes an animation listener. * Must be called on the UI thread. * * @param listener The listener to remove. */ Loading @@ -296,14 +329,15 @@ public final class Choreographer extends Handler { Log.d(TAG, "Removing onAnimate listener: " + listener); } synchronized (mLock) { mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class, mOnAnimateListeners, listener); stopTimingLoopIfNoListeners(); stopTimingLoopIfNoListenersLocked(); } } /** * Adds a draw listener. * Must be called on the UI thread. * * @param listener The listener to add. */ Loading @@ -316,9 +350,11 @@ public final class Choreographer extends Handler { Log.d(TAG, "Adding onDraw listener: " + listener); } synchronized (mLock) { mOnDrawListeners = ArrayUtils.appendElement(OnDrawListener.class, mOnDrawListeners, listener); } } /** * Removes a draw listener. Loading @@ -335,12 +371,14 @@ public final class Choreographer extends Handler { Log.d(TAG, "Removing onDraw listener: " + listener); } synchronized (mLock) { mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class, mOnDrawListeners, listener); stopTimingLoopIfNoListeners(); stopTimingLoopIfNoListenersLocked(); } } private void stopTimingLoopIfNoListeners() { private void stopTimingLoopIfNoListenersLocked() { if (mOnDrawListeners == null && mOnAnimateListeners == null) { if (DEBUG) { Log.d(TAG, "Stopping timing loop."); Loading Loading
core/java/android/view/Choreographer.java +88 −50 Original line number Diff line number Diff line Loading @@ -27,6 +27,10 @@ import android.util.Log; /** * Coordinates animations and drawing for UI on a particular thread. * * This object is thread-safe. Other threads can add and remove listeners * or schedule work to occur at a later time on the UI thread. * * @hide */ public final class Choreographer extends Handler { Loading @@ -44,7 +48,7 @@ public final class Choreographer extends Handler { private static final long DEFAULT_FRAME_DELAY = 10; // The number of milliseconds between animation frames. private static long sFrameDelay = DEFAULT_FRAME_DELAY; private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY; // Thread local storage for the choreographer. private static final ThreadLocal<Choreographer> sThreadInstance = Loading Loading @@ -75,6 +79,8 @@ public final class Choreographer extends Handler { private static final int MSG_DO_ANIMATION = 0; private static final int MSG_DO_DRAW = 1; private final Object mLock = new Object(); private final Looper mLooper; private OnAnimateListener[] mOnAnimateListeners; Loading Loading @@ -138,9 +144,14 @@ public final class Choreographer extends Handler { /** * Schedules animation (and drawing) to occur on the next frame synchronization boundary. * Must be called on the UI thread. */ public void scheduleAnimation() { synchronized (mLock) { scheduleAnimationLocked(); } } private void scheduleAnimationLocked() { if (!mAnimationScheduled) { mAnimationScheduled = true; if (USE_VSYNC) { Loading @@ -163,23 +174,26 @@ public final class Choreographer extends Handler { } /** * Return true if {@link #scheduleAnimation()} has been called but * Returns true if {@link #scheduleAnimation()} has been called but * {@link OnAnimateListener#onAnimate() OnAnimateListener.onAnimate()} has * not yet been called. */ public boolean isAnimationScheduled() { synchronized (mLock) { return mAnimationScheduled; } } /** * Schedules drawing to occur on the next frame synchronization boundary. * Must be called on the UI thread. */ public void scheduleDraw() { synchronized (mLock) { if (!mDrawScheduled) { mDrawScheduled = true; if (USE_ANIMATION_TIMER_FOR_DRAW) { scheduleAnimation(); scheduleAnimationLocked(); } else { if (DEBUG) { Log.d(TAG, "Scheduling draw immediately."); Loading @@ -188,15 +202,18 @@ public final class Choreographer extends Handler { } } } } /** * Return true if {@link #scheduleDraw()} has been called but * Returns true if {@link #scheduleDraw()} has been called but * {@link OnDrawListener#onDraw() OnDrawListener.onDraw()} has * not yet been called. */ public boolean isDrawScheduled() { synchronized (mLock) { return mDrawScheduled; } } @Override public void handleMessage(Message msg) { Loading @@ -211,17 +228,32 @@ public final class Choreographer extends Handler { } private void doAnimation() { if (mAnimationScheduled) { doAnimationInner(); if (USE_ANIMATION_TIMER_FOR_DRAW) { doDraw(); } } private void doAnimationInner() { final long start; final OnAnimateListener[] listeners; synchronized (mLock) { if (!mAnimationScheduled) { return; // no work to do } mAnimationScheduled = false; final long start = SystemClock.uptimeMillis(); start = SystemClock.uptimeMillis(); if (DEBUG) { Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime) + " ms have elapsed since previous animation."); } mLastAnimationTime = start; final OnAnimateListener[] listeners = mOnAnimateListeners; listeners = mOnAnimateListeners; } if (listeners != null) { for (int i = 0; i < listeners.length; i++) { listeners[i].onAnimate(); Loading @@ -233,23 +265,25 @@ public final class Choreographer extends Handler { } } if (USE_ANIMATION_TIMER_FOR_DRAW) { doDraw(); } } private void doDraw() { if (mDrawScheduled) { final long start; final OnDrawListener[] listeners; synchronized (mLock) { if (!mDrawScheduled) { return; // no work to do } mDrawScheduled = false; final long start = SystemClock.uptimeMillis(); start = SystemClock.uptimeMillis(); if (DEBUG) { Log.d(TAG, "Performing draw: " + Math.max(0, start - mLastDrawTime) + " ms have elapsed since previous draw."); } mLastDrawTime = start; final OnDrawListener[] listeners = mOnDrawListeners; listeners = mOnDrawListeners; } if (listeners != null) { for (int i = 0; i < listeners.length; i++) { listeners[i].onDraw(); Loading @@ -260,11 +294,9 @@ public final class Choreographer extends Handler { Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms."); } } } /** * Adds an animation listener. * Must be called on the UI thread. * * @param listener The listener to add. */ Loading @@ -277,13 +309,14 @@ public final class Choreographer extends Handler { Log.d(TAG, "Adding onAnimate listener: " + listener); } synchronized (mLock) { mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class, mOnAnimateListeners, listener); } } /** * Removes an animation listener. * Must be called on the UI thread. * * @param listener The listener to remove. */ Loading @@ -296,14 +329,15 @@ public final class Choreographer extends Handler { Log.d(TAG, "Removing onAnimate listener: " + listener); } synchronized (mLock) { mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class, mOnAnimateListeners, listener); stopTimingLoopIfNoListeners(); stopTimingLoopIfNoListenersLocked(); } } /** * Adds a draw listener. * Must be called on the UI thread. * * @param listener The listener to add. */ Loading @@ -316,9 +350,11 @@ public final class Choreographer extends Handler { Log.d(TAG, "Adding onDraw listener: " + listener); } synchronized (mLock) { mOnDrawListeners = ArrayUtils.appendElement(OnDrawListener.class, mOnDrawListeners, listener); } } /** * Removes a draw listener. Loading @@ -335,12 +371,14 @@ public final class Choreographer extends Handler { Log.d(TAG, "Removing onDraw listener: " + listener); } synchronized (mLock) { mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class, mOnDrawListeners, listener); stopTimingLoopIfNoListeners(); stopTimingLoopIfNoListenersLocked(); } } private void stopTimingLoopIfNoListeners() { private void stopTimingLoopIfNoListenersLocked() { if (mOnDrawListeners == null && mOnAnimateListeners == null) { if (DEBUG) { Log.d(TAG, "Stopping timing loop."); Loading