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

Commit c4c0a22a authored by Jeff Brown's avatar Jeff Brown Committed by Android (Google) Code Review
Browse files

Merge "Simplify Choreographer API."

parents 019ace3e 4a06c800
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;
    }