Loading core/java/android/view/Choreographer.java +312 −126 Original line number Diff line number Diff line Loading @@ -37,7 +37,7 @@ import android.util.Log; * * @hide */ public final class Choreographer extends Handler { public final class Choreographer { private static final String TAG = "Choreographer"; private static final boolean DEBUG = false; Loading Loading @@ -92,10 +92,16 @@ public final class Choreographer extends Handler { private final Object mLock = new Object(); private final Looper mLooper; private final FrameHandler mHandler; private Callback mCallbackPool; private OnAnimateListener[] mOnAnimateListeners; private OnDrawListener[] mOnDrawListeners; private Callback mOnAnimateCallbacks; private Callback mOnDrawCallbacks; private boolean mAnimationScheduled; private boolean mDrawScheduled; private boolean mFrameDisplayEventReceiverNeeded; Loading @@ -104,8 +110,8 @@ public final class Choreographer extends Handler { private long mLastDrawTime; private Choreographer(Looper looper) { super(looper); mLooper = looper; mHandler = new FrameHandler(looper); mLastAnimationTime = Long.MIN_VALUE; mLastDrawTime = Long.MIN_VALUE; } Loading Loading @@ -158,12 +164,13 @@ public final class Choreographer extends Handler { */ public void scheduleAnimation() { synchronized (mLock) { scheduleAnimationLocked(); scheduleAnimationLocked(false); } } private void scheduleAnimationLocked() { if (!mAnimationScheduled) { private void scheduleAnimationLocked(boolean force) { if (!mAnimationScheduled && (force || mOnAnimateListeners != null || mOnAnimateCallbacks != null)) { mAnimationScheduled = true; if (USE_VSYNC) { if (DEBUG) { Loading @@ -176,13 +183,14 @@ public final class Choreographer extends Handler { if (!mFrameDisplayEventReceiverNeeded) { mFrameDisplayEventReceiverNeeded = true; if (mFrameDisplayEventReceiver != null) { removeMessages(MSG_DO_DISPOSE_RECEIVER); mHandler.removeMessages(MSG_DO_DISPOSE_RECEIVER); } } if (isRunningOnLooperThreadLocked()) { doScheduleVsyncLocked(); } else { sendMessageAtFrontOfQueue(obtainMessage(MSG_DO_SCHEDULE_VSYNC)); mHandler.sendMessageAtFrontOfQueue( mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC)); } } else { final long now = SystemClock.uptimeMillis(); Loading @@ -190,7 +198,7 @@ public final class Choreographer extends Handler { if (DEBUG) { Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms."); } sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime); mHandler.sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime); } } } Loading @@ -212,16 +220,21 @@ public final class Choreographer extends Handler { */ public void scheduleDraw() { synchronized (mLock) { if (!mDrawScheduled) { scheduleDrawLocked(); } } private void scheduleDrawLocked() { if (!mDrawScheduled && (mOnDrawListeners != null || mOnDrawCallbacks != null)) { mDrawScheduled = true; if (USE_ANIMATION_TIMER_FOR_DRAW) { scheduleAnimationLocked(); scheduleAnimationLocked(true); } else { if (DEBUG) { Log.d(TAG, "Scheduling draw immediately."); } sendEmptyMessage(MSG_DO_DRAW); } mHandler.sendEmptyMessage(MSG_DO_DRAW); } } } Loading @@ -237,25 +250,165 @@ public final class Choreographer extends Handler { } } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_DO_ANIMATION: doAnimation(); break; case MSG_DO_DRAW: doDraw(); break; case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; case MSG_DO_DISPOSE_RECEIVER: doDisposeReceiver(); break; /** * 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); } private void doAnimation() { 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 */ public void postOnAnimateCallback(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } synchronized (mLock) { mOnAnimateCallbacks = addCallbackLocked(mOnAnimateCallbacks, runnable); scheduleAnimationLocked(false); } } /** * Removes an animation callback. * Does nothing if the specified animation callback has not been posted or has already * been removed. * * @param runnable The animation callback to remove. * * @see #postOnAnimateCallback */ public void removeOnAnimateCallback(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } synchronized (mLock) { mOnAnimateCallbacks = removeCallbackLocked(mOnAnimateCallbacks, runnable); stopTimingLoopIfNoListenersOrCallbacksLocked(); } } /** * Posts a callback to run on the next draw cycle and schedules a draw cycle. * The callback only runs once and then is automatically removed. * * @param runnable The callback to run during the next draw cycle. * * @see #removeOnDrawCallback */ public void postOnDrawCallback(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } synchronized (mLock) { mOnDrawCallbacks = addCallbackLocked(mOnDrawCallbacks, runnable); scheduleDrawLocked(); } } /** * Removes a draw callback. * Does nothing if the specified draw callback has not been posted or has already * been removed. * * @param runnable The draw callback to remove. * * @see #postOnDrawCallback */ public void removeOnDrawCallback(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } synchronized (mLock) { mOnDrawCallbacks = removeCallbackLocked(mOnDrawCallbacks, runnable); stopTimingLoopIfNoListenersOrCallbacksLocked(); } } void doAnimation() { doAnimationInner(); if (USE_ANIMATION_TIMER_FOR_DRAW) { Loading @@ -263,9 +416,10 @@ public final class Choreographer extends Handler { } } private void doAnimationInner() { void doAnimationInner() { final long start; final OnAnimateListener[] listeners; final Callback callbacks; synchronized (mLock) { if (!mAnimationScheduled) { return; // no work to do Loading @@ -280,6 +434,8 @@ public final class Choreographer extends Handler { mLastAnimationTime = start; listeners = mOnAnimateListeners; callbacks = mOnAnimateCallbacks; mOnAnimateCallbacks = null; } if (listeners != null) { Loading @@ -288,14 +444,22 @@ public final class Choreographer extends Handler { } } if (callbacks != null) { runCallbacks(callbacks); synchronized (mLock) { recycleCallbacksLocked(callbacks); } } if (DEBUG) { Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms."); } } private void doDraw() { void doDraw() { final long start; final OnDrawListener[] listeners; final Callback callbacks; synchronized (mLock) { if (!mDrawScheduled) { return; // no work to do Loading @@ -310,6 +474,8 @@ public final class Choreographer extends Handler { mLastDrawTime = start; listeners = mOnDrawListeners; callbacks = mOnDrawCallbacks; mOnDrawCallbacks = null; } if (listeners != null) { Loading @@ -318,12 +484,19 @@ public final class Choreographer extends Handler { } } if (callbacks != null) { runCallbacks(callbacks); synchronized (mLock) { recycleCallbacksLocked(callbacks); } } if (DEBUG) { Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms."); } } private void doScheduleVsync() { void doScheduleVsync() { synchronized (mLock) { doScheduleVsyncLocked(); } Loading @@ -338,7 +511,7 @@ public final class Choreographer extends Handler { } } private void doDisposeReceiver() { void doDisposeReceiver() { synchronized (mLock) { if (!mFrameDisplayEventReceiverNeeded && mFrameDisplayEventReceiver != null) { mFrameDisplayEventReceiver.dispose(); Loading @@ -347,127 +520,111 @@ public final class Choreographer extends Handler { } } /** * 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"); } private void stopTimingLoopIfNoListenersOrCallbacksLocked() { if (mOnAnimateListeners == null && mOnDrawListeners == null && mOnAnimateCallbacks == null && mOnDrawCallbacks == null) { if (DEBUG) { Log.d(TAG, "Adding onAnimate listener: " + listener); } synchronized (mLock) { mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class, mOnAnimateListeners, listener); } Log.d(TAG, "Stopping timing loop."); } /** * 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 (mAnimationScheduled) { mAnimationScheduled = false; if (USE_VSYNC) { mHandler.removeMessages(MSG_DO_SCHEDULE_VSYNC); } else { mHandler.removeMessages(MSG_DO_ANIMATION); } if (DEBUG) { Log.d(TAG, "Removing onAnimate listener: " + listener); } synchronized (mLock) { mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class, mOnAnimateListeners, listener); stopTimingLoopIfNoListenersLocked(); if (mDrawScheduled) { mDrawScheduled = false; if (!USE_ANIMATION_TIMER_FOR_DRAW) { mHandler.removeMessages(MSG_DO_DRAW); } } /** * 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"); // Post a message to dispose the display event receiver if we haven't needed // it again after a certain amount of time has elapsed. Another reason to // defer disposal is that it is possible for use to attempt to dispose the // receiver while handling a vsync event that it dispatched, which might // cause a few problems... if (mFrameDisplayEventReceiverNeeded) { mFrameDisplayEventReceiverNeeded = false; if (mFrameDisplayEventReceiver != null) { mHandler.sendEmptyMessageDelayed(MSG_DO_DISPOSE_RECEIVER, DISPOSE_RECEIVER_DELAY); } 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"); private boolean isRunningOnLooperThreadLocked() { return Looper.myLooper() == mLooper; } if (DEBUG) { Log.d(TAG, "Removing onDraw listener: " + listener); private Callback addCallbackLocked(Callback head, Runnable runnable) { Callback callback = obtainCallbackLocked(runnable); if (head == null) { return callback; } synchronized (mLock) { mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class, mOnDrawListeners, listener); stopTimingLoopIfNoListenersLocked(); Callback tail = head; while (tail.next != null) { tail = tail.next; } tail.next = callback; return head; } private void stopTimingLoopIfNoListenersLocked() { if (mOnDrawListeners == null && mOnAnimateListeners == null) { if (DEBUG) { Log.d(TAG, "Stopping timing loop."); private Callback removeCallbackLocked(Callback head, Runnable runnable) { Callback predecessor = null; for (Callback callback = head; callback != null;) { final Callback next = callback.next; if (callback.runnable == runnable) { if (predecessor != null) { predecessor.next = next; } else { head = next; } if (mAnimationScheduled) { mAnimationScheduled = false; if (USE_VSYNC) { removeMessages(MSG_DO_SCHEDULE_VSYNC); recycleCallbackLocked(callback); } else { removeMessages(MSG_DO_ANIMATION); predecessor = callback; } callback = next; } return head; } if (mDrawScheduled) { mDrawScheduled = false; if (!USE_ANIMATION_TIMER_FOR_DRAW) { removeMessages(MSG_DO_DRAW); private void runCallbacks(Callback head) { while (head != null) { head.runnable.run(); head = head.next; } } // Post a message to dispose the display event receiver if we haven't needed // it again after a certain amount of time has elapsed. Another reason to // defer disposal is that it is possible for use to attempt to dispose the // receiver while handling a vsync event that it dispatched, which might // cause a few problems... if (mFrameDisplayEventReceiverNeeded) { mFrameDisplayEventReceiverNeeded = false; if (mFrameDisplayEventReceiver != null) { sendEmptyMessageDelayed(MSG_DO_DISPOSE_RECEIVER, DISPOSE_RECEIVER_DELAY); private void recycleCallbacksLocked(Callback head) { while (head != null) { final Callback next = head.next; recycleCallbackLocked(head); head = next; } } private Callback obtainCallbackLocked(Runnable runnable) { Callback callback = mCallbackPool; if (callback == null) { callback = new Callback(); } else { mCallbackPool = callback.next; callback.next = null; } callback.runnable = runnable; return callback; } private boolean isRunningOnLooperThreadLocked() { return Looper.myLooper() == mLooper; private void recycleCallbackLocked(Callback callback) { callback.runnable = null; callback.next = mCallbackPool; mCallbackPool = callback; } /** Loading @@ -490,6 +647,30 @@ public final class Choreographer extends Handler { public void onDraw(); } private final class FrameHandler extends Handler { public FrameHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_DO_ANIMATION: doAnimation(); break; case MSG_DO_DRAW: doDraw(); break; case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; case MSG_DO_DISPOSE_RECEIVER: doDisposeReceiver(); break; } } } private final class FrameDisplayEventReceiver extends DisplayEventReceiver { public FrameDisplayEventReceiver(Looper looper) { super(looper); Loading @@ -500,4 +681,9 @@ public final class Choreographer extends Handler { doAnimation(); } } private static final class Callback { public Callback next; public Runnable runnable; } } Loading
core/java/android/view/Choreographer.java +312 −126 Original line number Diff line number Diff line Loading @@ -37,7 +37,7 @@ import android.util.Log; * * @hide */ public final class Choreographer extends Handler { public final class Choreographer { private static final String TAG = "Choreographer"; private static final boolean DEBUG = false; Loading Loading @@ -92,10 +92,16 @@ public final class Choreographer extends Handler { private final Object mLock = new Object(); private final Looper mLooper; private final FrameHandler mHandler; private Callback mCallbackPool; private OnAnimateListener[] mOnAnimateListeners; private OnDrawListener[] mOnDrawListeners; private Callback mOnAnimateCallbacks; private Callback mOnDrawCallbacks; private boolean mAnimationScheduled; private boolean mDrawScheduled; private boolean mFrameDisplayEventReceiverNeeded; Loading @@ -104,8 +110,8 @@ public final class Choreographer extends Handler { private long mLastDrawTime; private Choreographer(Looper looper) { super(looper); mLooper = looper; mHandler = new FrameHandler(looper); mLastAnimationTime = Long.MIN_VALUE; mLastDrawTime = Long.MIN_VALUE; } Loading Loading @@ -158,12 +164,13 @@ public final class Choreographer extends Handler { */ public void scheduleAnimation() { synchronized (mLock) { scheduleAnimationLocked(); scheduleAnimationLocked(false); } } private void scheduleAnimationLocked() { if (!mAnimationScheduled) { private void scheduleAnimationLocked(boolean force) { if (!mAnimationScheduled && (force || mOnAnimateListeners != null || mOnAnimateCallbacks != null)) { mAnimationScheduled = true; if (USE_VSYNC) { if (DEBUG) { Loading @@ -176,13 +183,14 @@ public final class Choreographer extends Handler { if (!mFrameDisplayEventReceiverNeeded) { mFrameDisplayEventReceiverNeeded = true; if (mFrameDisplayEventReceiver != null) { removeMessages(MSG_DO_DISPOSE_RECEIVER); mHandler.removeMessages(MSG_DO_DISPOSE_RECEIVER); } } if (isRunningOnLooperThreadLocked()) { doScheduleVsyncLocked(); } else { sendMessageAtFrontOfQueue(obtainMessage(MSG_DO_SCHEDULE_VSYNC)); mHandler.sendMessageAtFrontOfQueue( mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC)); } } else { final long now = SystemClock.uptimeMillis(); Loading @@ -190,7 +198,7 @@ public final class Choreographer extends Handler { if (DEBUG) { Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms."); } sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime); mHandler.sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime); } } } Loading @@ -212,16 +220,21 @@ public final class Choreographer extends Handler { */ public void scheduleDraw() { synchronized (mLock) { if (!mDrawScheduled) { scheduleDrawLocked(); } } private void scheduleDrawLocked() { if (!mDrawScheduled && (mOnDrawListeners != null || mOnDrawCallbacks != null)) { mDrawScheduled = true; if (USE_ANIMATION_TIMER_FOR_DRAW) { scheduleAnimationLocked(); scheduleAnimationLocked(true); } else { if (DEBUG) { Log.d(TAG, "Scheduling draw immediately."); } sendEmptyMessage(MSG_DO_DRAW); } mHandler.sendEmptyMessage(MSG_DO_DRAW); } } } Loading @@ -237,25 +250,165 @@ public final class Choreographer extends Handler { } } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_DO_ANIMATION: doAnimation(); break; case MSG_DO_DRAW: doDraw(); break; case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; case MSG_DO_DISPOSE_RECEIVER: doDisposeReceiver(); break; /** * 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); } private void doAnimation() { 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 */ public void postOnAnimateCallback(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } synchronized (mLock) { mOnAnimateCallbacks = addCallbackLocked(mOnAnimateCallbacks, runnable); scheduleAnimationLocked(false); } } /** * Removes an animation callback. * Does nothing if the specified animation callback has not been posted or has already * been removed. * * @param runnable The animation callback to remove. * * @see #postOnAnimateCallback */ public void removeOnAnimateCallback(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } synchronized (mLock) { mOnAnimateCallbacks = removeCallbackLocked(mOnAnimateCallbacks, runnable); stopTimingLoopIfNoListenersOrCallbacksLocked(); } } /** * Posts a callback to run on the next draw cycle and schedules a draw cycle. * The callback only runs once and then is automatically removed. * * @param runnable The callback to run during the next draw cycle. * * @see #removeOnDrawCallback */ public void postOnDrawCallback(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } synchronized (mLock) { mOnDrawCallbacks = addCallbackLocked(mOnDrawCallbacks, runnable); scheduleDrawLocked(); } } /** * Removes a draw callback. * Does nothing if the specified draw callback has not been posted or has already * been removed. * * @param runnable The draw callback to remove. * * @see #postOnDrawCallback */ public void removeOnDrawCallback(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } synchronized (mLock) { mOnDrawCallbacks = removeCallbackLocked(mOnDrawCallbacks, runnable); stopTimingLoopIfNoListenersOrCallbacksLocked(); } } void doAnimation() { doAnimationInner(); if (USE_ANIMATION_TIMER_FOR_DRAW) { Loading @@ -263,9 +416,10 @@ public final class Choreographer extends Handler { } } private void doAnimationInner() { void doAnimationInner() { final long start; final OnAnimateListener[] listeners; final Callback callbacks; synchronized (mLock) { if (!mAnimationScheduled) { return; // no work to do Loading @@ -280,6 +434,8 @@ public final class Choreographer extends Handler { mLastAnimationTime = start; listeners = mOnAnimateListeners; callbacks = mOnAnimateCallbacks; mOnAnimateCallbacks = null; } if (listeners != null) { Loading @@ -288,14 +444,22 @@ public final class Choreographer extends Handler { } } if (callbacks != null) { runCallbacks(callbacks); synchronized (mLock) { recycleCallbacksLocked(callbacks); } } if (DEBUG) { Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms."); } } private void doDraw() { void doDraw() { final long start; final OnDrawListener[] listeners; final Callback callbacks; synchronized (mLock) { if (!mDrawScheduled) { return; // no work to do Loading @@ -310,6 +474,8 @@ public final class Choreographer extends Handler { mLastDrawTime = start; listeners = mOnDrawListeners; callbacks = mOnDrawCallbacks; mOnDrawCallbacks = null; } if (listeners != null) { Loading @@ -318,12 +484,19 @@ public final class Choreographer extends Handler { } } if (callbacks != null) { runCallbacks(callbacks); synchronized (mLock) { recycleCallbacksLocked(callbacks); } } if (DEBUG) { Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms."); } } private void doScheduleVsync() { void doScheduleVsync() { synchronized (mLock) { doScheduleVsyncLocked(); } Loading @@ -338,7 +511,7 @@ public final class Choreographer extends Handler { } } private void doDisposeReceiver() { void doDisposeReceiver() { synchronized (mLock) { if (!mFrameDisplayEventReceiverNeeded && mFrameDisplayEventReceiver != null) { mFrameDisplayEventReceiver.dispose(); Loading @@ -347,127 +520,111 @@ public final class Choreographer extends Handler { } } /** * 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"); } private void stopTimingLoopIfNoListenersOrCallbacksLocked() { if (mOnAnimateListeners == null && mOnDrawListeners == null && mOnAnimateCallbacks == null && mOnDrawCallbacks == null) { if (DEBUG) { Log.d(TAG, "Adding onAnimate listener: " + listener); } synchronized (mLock) { mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class, mOnAnimateListeners, listener); } Log.d(TAG, "Stopping timing loop."); } /** * 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 (mAnimationScheduled) { mAnimationScheduled = false; if (USE_VSYNC) { mHandler.removeMessages(MSG_DO_SCHEDULE_VSYNC); } else { mHandler.removeMessages(MSG_DO_ANIMATION); } if (DEBUG) { Log.d(TAG, "Removing onAnimate listener: " + listener); } synchronized (mLock) { mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class, mOnAnimateListeners, listener); stopTimingLoopIfNoListenersLocked(); if (mDrawScheduled) { mDrawScheduled = false; if (!USE_ANIMATION_TIMER_FOR_DRAW) { mHandler.removeMessages(MSG_DO_DRAW); } } /** * 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"); // Post a message to dispose the display event receiver if we haven't needed // it again after a certain amount of time has elapsed. Another reason to // defer disposal is that it is possible for use to attempt to dispose the // receiver while handling a vsync event that it dispatched, which might // cause a few problems... if (mFrameDisplayEventReceiverNeeded) { mFrameDisplayEventReceiverNeeded = false; if (mFrameDisplayEventReceiver != null) { mHandler.sendEmptyMessageDelayed(MSG_DO_DISPOSE_RECEIVER, DISPOSE_RECEIVER_DELAY); } 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"); private boolean isRunningOnLooperThreadLocked() { return Looper.myLooper() == mLooper; } if (DEBUG) { Log.d(TAG, "Removing onDraw listener: " + listener); private Callback addCallbackLocked(Callback head, Runnable runnable) { Callback callback = obtainCallbackLocked(runnable); if (head == null) { return callback; } synchronized (mLock) { mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class, mOnDrawListeners, listener); stopTimingLoopIfNoListenersLocked(); Callback tail = head; while (tail.next != null) { tail = tail.next; } tail.next = callback; return head; } private void stopTimingLoopIfNoListenersLocked() { if (mOnDrawListeners == null && mOnAnimateListeners == null) { if (DEBUG) { Log.d(TAG, "Stopping timing loop."); private Callback removeCallbackLocked(Callback head, Runnable runnable) { Callback predecessor = null; for (Callback callback = head; callback != null;) { final Callback next = callback.next; if (callback.runnable == runnable) { if (predecessor != null) { predecessor.next = next; } else { head = next; } if (mAnimationScheduled) { mAnimationScheduled = false; if (USE_VSYNC) { removeMessages(MSG_DO_SCHEDULE_VSYNC); recycleCallbackLocked(callback); } else { removeMessages(MSG_DO_ANIMATION); predecessor = callback; } callback = next; } return head; } if (mDrawScheduled) { mDrawScheduled = false; if (!USE_ANIMATION_TIMER_FOR_DRAW) { removeMessages(MSG_DO_DRAW); private void runCallbacks(Callback head) { while (head != null) { head.runnable.run(); head = head.next; } } // Post a message to dispose the display event receiver if we haven't needed // it again after a certain amount of time has elapsed. Another reason to // defer disposal is that it is possible for use to attempt to dispose the // receiver while handling a vsync event that it dispatched, which might // cause a few problems... if (mFrameDisplayEventReceiverNeeded) { mFrameDisplayEventReceiverNeeded = false; if (mFrameDisplayEventReceiver != null) { sendEmptyMessageDelayed(MSG_DO_DISPOSE_RECEIVER, DISPOSE_RECEIVER_DELAY); private void recycleCallbacksLocked(Callback head) { while (head != null) { final Callback next = head.next; recycleCallbackLocked(head); head = next; } } private Callback obtainCallbackLocked(Runnable runnable) { Callback callback = mCallbackPool; if (callback == null) { callback = new Callback(); } else { mCallbackPool = callback.next; callback.next = null; } callback.runnable = runnable; return callback; } private boolean isRunningOnLooperThreadLocked() { return Looper.myLooper() == mLooper; private void recycleCallbackLocked(Callback callback) { callback.runnable = null; callback.next = mCallbackPool; mCallbackPool = callback; } /** Loading @@ -490,6 +647,30 @@ public final class Choreographer extends Handler { public void onDraw(); } private final class FrameHandler extends Handler { public FrameHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_DO_ANIMATION: doAnimation(); break; case MSG_DO_DRAW: doDraw(); break; case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; case MSG_DO_DISPOSE_RECEIVER: doDisposeReceiver(); break; } } } private final class FrameDisplayEventReceiver extends DisplayEventReceiver { public FrameDisplayEventReceiver(Looper looper) { super(looper); Loading @@ -500,4 +681,9 @@ public final class Choreographer extends Handler { doAnimation(); } } private static final class Callback { public Callback next; public Runnable runnable; } }