Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a06c800 authored by Jeff Brown's avatar Jeff Brown
Browse files

Simplify Choreographer API.

Removed the listeners and schedule animation / draw methods.
Instead all requests are posted as one-shot callbacks, which is a
better match for how clients actually use the Choreographer.

Bug: 5721047
Change-Id: I113180b2713a300e4444d0d987f52b8157b7ac15
parent fef3d62b
Loading
Loading
Loading
Loading
+9 −15
Original line number Diff line number Diff line
@@ -522,8 +522,7 @@ public class ValueAnimator extends Animator {
     * animations possible.
     *
     */
    private static class AnimationHandler extends Handler
            implements Choreographer.OnAnimateListener {
    private static class AnimationHandler extends Handler implements Runnable {
        // The per-thread list of all active animations
        private final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();

@@ -539,7 +538,7 @@ public class ValueAnimator extends Animator {
        private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>();

        private final Choreographer mChoreographer;
        private boolean mIsChoreographed;
        private boolean mAnimationScheduled;

        private AnimationHandler() {
            mChoreographer = Choreographer.getInstance();
@@ -644,22 +643,17 @@ public class ValueAnimator extends Animator {

            // If there are still active or delayed animations, schedule a future call to
            // onAnimate to process the next frame of the animations.
            if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
                if (!mIsChoreographed) {
                    mIsChoreographed = true;
                    mChoreographer.addOnAnimateListener(this);
                }
                mChoreographer.scheduleAnimation();
            } else {
                if (mIsChoreographed) {
                    mIsChoreographed = false;
                    mChoreographer.removeOnAnimateListener(this);
                }
            if (!mAnimationScheduled
                    && (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty())) {
                mChoreographer.postAnimationCallback(this);
                mAnimationScheduled = true;
            }
        }

        // Called by the Choreographer.
        @Override
        public void onAnimate() {
        public void run() {
            mAnimationScheduled = false;
            doAnimationFrame();
        }
    }
+75 −244
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package android.view;

import com.android.internal.util.ArrayUtils;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -28,8 +26,8 @@ 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.
 * This object is thread-safe.  Other threads can post callbacks to run at a later time
 * on the UI thread.
 *
 * Ensuring thread-safety is a little tricky because the {@link DisplayEventReceiver}
 * can only be accessed from the UI thread so operations that touch the event receiver
@@ -42,8 +40,8 @@ public final class Choreographer {
    private static final boolean DEBUG = false;

    // Amount of time in ms to wait before actually disposing of the display event
    // receiver after all listeners have been removed.
    private static final long DISPOSE_RECEIVER_DELAY = 200;
    // receiver when it has not been needed for some time.
    private static final long DISPOSE_RECEIVER_DELAY = 30 * 1000;

    // The default amount of time in ms between animation frames.
    // When vsync is not enabled, we want to have some idea of how long we should
@@ -96,11 +94,8 @@ public final class Choreographer {

    private Callback mCallbackPool;

    private OnAnimateListener[] mOnAnimateListeners;
    private OnDrawListener[] mOnDrawListeners;

    private Callback mOnAnimateCallbacks;
    private Callback mOnDrawCallbacks;
    private Callback mAnimationCallbacks;
    private Callback mDrawCallbacks;

    private boolean mAnimationScheduled;
    private boolean mDrawScheduled;
@@ -159,196 +154,21 @@ public final class Choreographer {
        sFrameDelay = frameDelay;
    }

    /**
     * Schedules animation (and drawing) to occur on the next frame synchronization boundary.
     */
    public void scheduleAnimation() {
        synchronized (mLock) {
            scheduleAnimationLocked(false);
        }
    }

    private void scheduleAnimationLocked(boolean force) {
        if (!mAnimationScheduled
                && (force || mOnAnimateListeners != null || mOnAnimateCallbacks != null)) {
            mAnimationScheduled = true;
            if (USE_VSYNC) {
                if (DEBUG) {
                    Log.d(TAG, "Scheduling vsync for animation.");
                }

                // If running on the Looper thread, then schedule the vsync immediately,
                // otherwise post a message to schedule the vsync from the UI thread
                // as soon as possible.
                if (!mFrameDisplayEventReceiverNeeded) {
                    mFrameDisplayEventReceiverNeeded = true;
                    if (mFrameDisplayEventReceiver != null) {
                        mHandler.removeMessages(MSG_DO_DISPOSE_RECEIVER);
                    }
                }
                if (isRunningOnLooperThreadLocked()) {
                    doScheduleVsyncLocked();
                } else {
                    mHandler.sendMessageAtFrontOfQueue(
                            mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC));
                }
            } else {
                final long now = SystemClock.uptimeMillis();
                final long nextAnimationTime = Math.max(mLastAnimationTime + sFrameDelay, now);
                if (DEBUG) {
                    Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms.");
                }
                mHandler.sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime);
            }
        }
    }

    /**
     * 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) {
            scheduleDrawLocked();
        }
    }

    private void scheduleDrawLocked() {
        if (!mDrawScheduled
                && (mOnDrawListeners != null || mOnDrawCallbacks != null)) {
            mDrawScheduled = true;
            if (USE_ANIMATION_TIMER_FOR_DRAW) {
                scheduleAnimationLocked(true);
            } else {
                if (DEBUG) {
                    Log.d(TAG, "Scheduling draw immediately.");
                }
                mHandler.sendEmptyMessage(MSG_DO_DRAW);
            }
        }
    }

    /**
     * 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;
        }
    }

    /**
     * Adds an animation listener.
     *
     * @param listener The listener to add.
     */
    public void addOnAnimateListener(OnAnimateListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }

        if (DEBUG) {
            Log.d(TAG, "Adding onAnimate listener: " + listener);
        }

        synchronized (mLock) {
            mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class,
                    mOnAnimateListeners, listener);
        }
    }

    /**
     * Removes an animation listener.
     *
     * @param listener The listener to remove.
     */
    public void removeOnAnimateListener(OnAnimateListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }

        if (DEBUG) {
            Log.d(TAG, "Removing onAnimate listener: " + listener);
        }

        synchronized (mLock) {
            mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class,
                    mOnAnimateListeners, listener);
            stopTimingLoopIfNoListenersOrCallbacksLocked();
        }
    }

    /**
     * Adds a draw listener.
     *
     * @param listener The listener to add.
     */
    public void addOnDrawListener(OnDrawListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }

        if (DEBUG) {
            Log.d(TAG, "Adding onDraw listener: " + listener);
        }

        synchronized (mLock) {
            mOnDrawListeners = ArrayUtils.appendElement(OnDrawListener.class,
                    mOnDrawListeners, listener);
        }
    }

    /**
     * Removes a draw listener.
     * Must be called on the UI thread.
     *
     * @param listener The listener to remove.
     */
    public void removeOnDrawListener(OnDrawListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }

        if (DEBUG) {
            Log.d(TAG, "Removing onDraw listener: " + listener);
        }

        synchronized (mLock) {
            mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class,
                    mOnDrawListeners, listener);
            stopTimingLoopIfNoListenersOrCallbacksLocked();
        }
    }


    /**
     * Posts a callback to run on the next animation cycle and schedules an animation cycle.
     * The callback only runs once and then is automatically removed.
     *
     * @param runnable The callback to run during the next animation cycle.
     *
     * @see #removeOnAnimateCallback
     * @see #removeAnimationCallback
     */
    public void postOnAnimateCallback(Runnable runnable) {
    public void postAnimationCallback(Runnable runnable) {
        if (runnable == null) {
            throw new IllegalArgumentException("runnable must not be null");
        }
        synchronized (mLock) {
            mOnAnimateCallbacks = addCallbackLocked(mOnAnimateCallbacks, runnable);
            scheduleAnimationLocked(false);
            mAnimationCallbacks = addCallbackLocked(mAnimationCallbacks, runnable);
            scheduleAnimationLocked();
        }
    }

@@ -359,15 +179,15 @@ public final class Choreographer {
     *
     * @param runnable The animation callback to remove.
     *
     * @see #postOnAnimateCallback
     * @see #postAnimationCallback
     */
    public void removeOnAnimateCallback(Runnable runnable) {
    public void removeAnimationCallback(Runnable runnable) {
        if (runnable == null) {
            throw new IllegalArgumentException("runnable must not be null");
        }
        synchronized (mLock) {
            mOnAnimateCallbacks = removeCallbackLocked(mOnAnimateCallbacks, runnable);
            stopTimingLoopIfNoListenersOrCallbacksLocked();
            mAnimationCallbacks = removeCallbackLocked(mAnimationCallbacks, runnable);
            stopTimingLoopIfNoCallbacksLocked();
        }
    }

@@ -377,14 +197,14 @@ public final class Choreographer {
     *
     * @param runnable The callback to run during the next draw cycle.
     *
     * @see #removeOnDrawCallback
     * @see #removeDrawCallback
     */
    public void postOnDrawCallback(Runnable runnable) {
    public void postDrawCallback(Runnable runnable) {
        if (runnable == null) {
            throw new IllegalArgumentException("runnable must not be null");
        }
        synchronized (mLock) {
            mOnDrawCallbacks = addCallbackLocked(mOnDrawCallbacks, runnable);
            mDrawCallbacks = addCallbackLocked(mDrawCallbacks, runnable);
            scheduleDrawLocked();
        }
    }
@@ -396,15 +216,63 @@ public final class Choreographer {
     *
     * @param runnable The draw callback to remove.
     *
     * @see #postOnDrawCallback
     * @see #postDrawCallback
     */
    public void removeOnDrawCallback(Runnable runnable) {
    public void removeDrawCallback(Runnable runnable) {
        if (runnable == null) {
            throw new IllegalArgumentException("runnable must not be null");
        }
        synchronized (mLock) {
            mOnDrawCallbacks = removeCallbackLocked(mOnDrawCallbacks, runnable);
            stopTimingLoopIfNoListenersOrCallbacksLocked();
            mDrawCallbacks = removeCallbackLocked(mDrawCallbacks, runnable);
            stopTimingLoopIfNoCallbacksLocked();
        }
    }

    private void scheduleAnimationLocked() {
        if (!mAnimationScheduled) {
            mAnimationScheduled = true;
            if (USE_VSYNC) {
                if (DEBUG) {
                    Log.d(TAG, "Scheduling vsync for animation.");
                }

                // If running on the Looper thread, then schedule the vsync immediately,
                // otherwise post a message to schedule the vsync from the UI thread
                // as soon as possible.
                if (!mFrameDisplayEventReceiverNeeded) {
                    mFrameDisplayEventReceiverNeeded = true;
                    if (mFrameDisplayEventReceiver != null) {
                        mHandler.removeMessages(MSG_DO_DISPOSE_RECEIVER);
                    }
                }
                if (isRunningOnLooperThreadLocked()) {
                    doScheduleVsyncLocked();
                } else {
                    mHandler.sendMessageAtFrontOfQueue(
                            mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC));
                }
            } else {
                final long now = SystemClock.uptimeMillis();
                final long nextAnimationTime = Math.max(mLastAnimationTime + sFrameDelay, now);
                if (DEBUG) {
                    Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms.");
                }
                mHandler.sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime);
            }
        }
    }

    private void scheduleDrawLocked() {
        if (!mDrawScheduled) {
            mDrawScheduled = true;
            if (USE_ANIMATION_TIMER_FOR_DRAW) {
                scheduleAnimationLocked();
            } else {
                if (DEBUG) {
                    Log.d(TAG, "Scheduling draw immediately.");
                }
                mHandler.sendEmptyMessage(MSG_DO_DRAW);
            }
        }
    }

@@ -418,7 +286,6 @@ public final class Choreographer {

    void doAnimationInner() {
        final long start;
        final OnAnimateListener[] listeners;
        final Callback callbacks;
        synchronized (mLock) {
            if (!mAnimationScheduled) {
@@ -433,15 +300,8 @@ public final class Choreographer {
            }
            mLastAnimationTime = start;

            listeners = mOnAnimateListeners;
            callbacks = mOnAnimateCallbacks;
            mOnAnimateCallbacks = null;
        }

        if (listeners != null) {
            for (int i = 0; i < listeners.length; i++) {
                listeners[i].onAnimate();
            }
            callbacks = mAnimationCallbacks;
            mAnimationCallbacks = null;
        }

        if (callbacks != null) {
@@ -458,7 +318,6 @@ public final class Choreographer {

    void doDraw() {
        final long start;
        final OnDrawListener[] listeners;
        final Callback callbacks;
        synchronized (mLock) {
            if (!mDrawScheduled) {
@@ -473,15 +332,8 @@ public final class Choreographer {
            }
            mLastDrawTime = start;

            listeners = mOnDrawListeners;
            callbacks = mOnDrawCallbacks;
            mOnDrawCallbacks = null;
        }

        if (listeners != null) {
            for (int i = 0; i < listeners.length; i++) {
                listeners[i].onDraw();
            }
            callbacks = mDrawCallbacks;
            mDrawCallbacks = null;
        }

        if (callbacks != null) {
@@ -520,9 +372,8 @@ public final class Choreographer {
        }
    }

    private void stopTimingLoopIfNoListenersOrCallbacksLocked() {
        if (mOnAnimateListeners == null && mOnDrawListeners == null
                && mOnAnimateCallbacks == null && mOnDrawCallbacks == null) {
    private void stopTimingLoopIfNoCallbacksLocked() {
        if (mAnimationCallbacks == null && mDrawCallbacks == null) {
            if (DEBUG) {
                Log.d(TAG, "Stopping timing loop.");
            }
@@ -627,26 +478,6 @@ public final class Choreographer {
        mCallbackPool = callback;
    }

    /**
     * Listens for animation frame timing events.
     */
    public static interface OnAnimateListener {
        /**
         * Called to animate properties before drawing the frame.
         */
        public void onAnimate();
    }

    /**
     * Listens for draw frame timing events.
     */
    public static interface OnDrawListener {
        /**
         * Called to draw the frame.
         */
        public void onDraw();
    }

    private final class FrameHandler extends Handler {
        public FrameHandler(Looper looper) {
            super(looper);
+29 −9
Original line number Diff line number Diff line
@@ -97,8 +97,7 @@ import java.util.List;
 */
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl extends Handler implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks,
        Choreographer.OnDrawListener {
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    private static final String TAG = "ViewRootImpl";
    private static final boolean DBG = false;
    private static final boolean LOCAL_LOGV = false;
@@ -463,8 +462,6 @@ public final class ViewRootImpl extends Handler implements ViewParent,
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mChoreographer.addOnDrawListener(this);

                mView = view;
                mFallbackEventHandler.setView(view);
                mWindowAttributes.copyFrom(attrs);
@@ -841,7 +838,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
    public void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mChoreographer.scheduleDraw();
            scheduleFrame();
        }
    }

@@ -849,8 +846,21 @@ public final class ViewRootImpl extends Handler implements ViewParent,
        mTraversalScheduled = false;
    }

    @Override
    public void onDraw() {
    void scheduleFrame() {
        if (!mFrameScheduled) {
            mChoreographer.postDrawCallback(mFrameRunnable);
            mFrameScheduled = true;
        }
    }

    void unscheduleFrame() {
        if (mFrameScheduled) {
            mFrameScheduled = false;
            mChoreographer.removeDrawCallback(mFrameRunnable);
        }
    }

    void doFrame() {
        if (mInputEventReceiver != null) {
            mInputEventReceiver.consumeBatchedInputEvents();
        }
@@ -2376,7 +2386,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
            mInputChannel = null;
        }

        mChoreographer.removeOnDrawListener(this);
        unscheduleFrame();
    }

    void updateConfiguration(Configuration config, boolean force) {
@@ -3923,6 +3933,16 @@ public final class ViewRootImpl extends Handler implements ViewParent,
        }
    }

    final class FrameRunnable implements Runnable {
        @Override
        public void run() {
            mFrameScheduled = false;
            doFrame();
        }
    }
    final FrameRunnable mFrameRunnable = new FrameRunnable();
    boolean mFrameScheduled;

    final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
@@ -3935,7 +3955,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,

        @Override
        public void onBatchedInputEventPending() {
            mChoreographer.scheduleDraw();
            scheduleFrame();
        }
    }
    WindowInputEventReceiver mInputEventReceiver;
+21 −11
Original line number Diff line number Diff line
@@ -142,8 +142,7 @@ import java.util.List;

/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs,
        Choreographer.OnAnimateListener {
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    static final String TAG = "WindowManager";
    static final boolean DEBUG = false;
    static final boolean DEBUG_ADD_REMOVE = false;
@@ -603,6 +602,18 @@ public class WindowManagerService extends IWindowManager.Stub
    }
    private LayoutAndSurfaceFields mInnerFields = new LayoutAndSurfaceFields();

    private final class AnimationRunnable implements Runnable {
        @Override
        public void run() {
            synchronized(mWindowMap) {
                mAnimationScheduled = false;
                performLayoutAndPlaceSurfacesLocked();
            }
        }
    }
    final AnimationRunnable mAnimationRunnable = new AnimationRunnable();
    boolean mAnimationScheduled;

    final class DragInputEventReceiver extends InputEventReceiver {
        public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
@@ -724,7 +735,6 @@ public class WindowManagerService extends IWindowManager.Stub
            Looper.prepare();
            WindowManagerService s = new WindowManagerService(mContext, mPM,
                    mHaveInputMethods, mAllowBootMessages);
            s.mChoreographer.addOnAnimateListener(s);
            android.os.Process.setThreadPriority(
                    android.os.Process.THREAD_PRIORITY_DISPLAY);
            android.os.Process.setCanSelfBackground(false);
@@ -5441,7 +5451,7 @@ public class WindowManagerService extends IWindowManager.Stub
                if (mScreenRotationAnimation.setRotation(rotation, mFxSession,
                        MAX_ANIMATION_DURATION, mTransitionAnimationScale,
                        mCurDisplayWidth, mCurDisplayHeight)) {
                    mChoreographer.scheduleAnimation();
                    scheduleAnimationLocked();
                }
            }
            Surface.setOrientation(0, rotation);
@@ -6882,7 +6892,7 @@ public class WindowManagerService extends IWindowManager.Stub

                case FORCE_GC: {
                    synchronized(mWindowMap) {
                        if (mChoreographer.isAnimationScheduled()) {
                        if (mAnimationScheduled) {
                            // If we are animating, don't do the gc now but
                            // delay a bit so we don't interrupt the animation.
                            mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC),
@@ -8980,7 +8990,7 @@ public class WindowManagerService extends IWindowManager.Stub
        if (needRelayout) {
            requestTraversalLocked();
        } else if (mInnerFields.mAnimating) {
            mChoreographer.scheduleAnimation();
            scheduleAnimationLocked();
        }

        // Finally update all input windows now that the window changes have stabilized.
@@ -9100,10 +9110,10 @@ public class WindowManagerService extends IWindowManager.Stub
        }
    }

    @Override
    public void onAnimate() {
        synchronized(mWindowMap) {
            performLayoutAndPlaceSurfacesLocked();
    void scheduleAnimationLocked() {
        if (!mAnimationScheduled) {
            mChoreographer.postAnimationCallback(mAnimationRunnable);
            mAnimationScheduled = true;
        }
    }

@@ -9423,7 +9433,7 @@ public class WindowManagerService extends IWindowManager.Stub
            if (DEBUG_ORIENTATION) Slog.i(TAG, "**** Dismissing screen rotation animation");
            if (mScreenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
                    mTransitionAnimationScale, mCurDisplayWidth, mCurDisplayHeight)) {
                mChoreographer.scheduleAnimation();
                scheduleAnimationLocked();
            } else {
                mScreenRotationAnimation = null;
                updateRotation = true;
+2 −2
Original line number Diff line number Diff line
@@ -1593,7 +1593,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
            mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true);
        }
        if (requestAnim) {
            mService.mChoreographer.scheduleAnimation();
            mService.scheduleAnimationLocked();
        }
        return true;
    }
@@ -1634,7 +1634,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
            }
        }
        if (requestAnim) {
            mService.mChoreographer.scheduleAnimation();
            mService.scheduleAnimationLocked();
        }
        return true;
    }