diff options
| author | 2012-02-14 19:41:32 -0800 | |
|---|---|---|
| committer | 2012-02-14 19:41:32 -0800 | |
| commit | fef3d62b16bccd9ef7a32c2e1da94f694b34c405 (patch) | |
| tree | 56a65ff3179b267310a904a12d5ca9efbe0e1b8a | |
| parent | 472ea60685c35b450ecf380d8946f10835c210cc (diff) | |
| parent | 968588573cf516fd8cf33c133d06f06c67ca1785 (diff) | |
Merge "Add support for posting Runnables to the Choreographer."
| -rw-r--r-- | core/java/android/view/Choreographer.java | 438 |
1 files changed, 312 insertions, 126 deletions
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 3e2d7fc60241..658da2f24cd9 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -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; @@ -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; @@ -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; } @@ -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) { @@ -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(); @@ -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); } } } @@ -212,16 +220,21 @@ public final class Choreographer extends Handler { */ public void scheduleDraw() { synchronized (mLock) { - if (!mDrawScheduled) { - mDrawScheduled = true; - if (USE_ANIMATION_TIMER_FOR_DRAW) { - scheduleAnimationLocked(); - } else { - if (DEBUG) { - Log.d(TAG, "Scheduling draw immediately."); - } - sendEmptyMessage(MSG_DO_DRAW); + 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); } } } @@ -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); + } + + 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(); } } - private void doAnimation() { + void doAnimation() { doAnimationInner(); if (USE_ANIMATION_TIMER_FOR_DRAW) { @@ -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 @@ -280,6 +434,8 @@ public final class Choreographer extends Handler { mLastAnimationTime = start; listeners = mOnAnimateListeners; + callbacks = mOnAnimateCallbacks; + mOnAnimateCallbacks = null; } if (listeners != null) { @@ -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 @@ -310,6 +474,8 @@ public final class Choreographer extends Handler { mLastDrawTime = start; listeners = mOnDrawListeners; + callbacks = mOnDrawCallbacks; + mOnDrawCallbacks = null; } if (listeners != null) { @@ -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(); } @@ -338,7 +511,7 @@ public final class Choreographer extends Handler { } } - private void doDisposeReceiver() { + void doDisposeReceiver() { synchronized (mLock) { if (!mFrameDisplayEventReceiverNeeded && mFrameDisplayEventReceiver != null) { mFrameDisplayEventReceiver.dispose(); @@ -347,91 +520,9 @@ 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"); - } - - 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); - stopTimingLoopIfNoListenersLocked(); - } - } - - /** - * 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); - stopTimingLoopIfNoListenersLocked(); - } - } - - private void stopTimingLoopIfNoListenersLocked() { - if (mOnDrawListeners == null && mOnAnimateListeners == null) { + private void stopTimingLoopIfNoListenersOrCallbacksLocked() { + if (mOnAnimateListeners == null && mOnDrawListeners == null + && mOnAnimateCallbacks == null && mOnDrawCallbacks == null) { if (DEBUG) { Log.d(TAG, "Stopping timing loop."); } @@ -439,16 +530,16 @@ public final class Choreographer extends Handler { if (mAnimationScheduled) { mAnimationScheduled = false; if (USE_VSYNC) { - removeMessages(MSG_DO_SCHEDULE_VSYNC); + mHandler.removeMessages(MSG_DO_SCHEDULE_VSYNC); } else { - removeMessages(MSG_DO_ANIMATION); + mHandler.removeMessages(MSG_DO_ANIMATION); } } if (mDrawScheduled) { mDrawScheduled = false; if (!USE_ANIMATION_TIMER_FOR_DRAW) { - removeMessages(MSG_DO_DRAW); + mHandler.removeMessages(MSG_DO_DRAW); } } @@ -460,7 +551,8 @@ public final class Choreographer extends Handler { if (mFrameDisplayEventReceiverNeeded) { mFrameDisplayEventReceiverNeeded = false; if (mFrameDisplayEventReceiver != null) { - sendEmptyMessageDelayed(MSG_DO_DISPOSE_RECEIVER, DISPOSE_RECEIVER_DELAY); + mHandler.sendEmptyMessageDelayed(MSG_DO_DISPOSE_RECEIVER, + DISPOSE_RECEIVER_DELAY); } } } @@ -470,6 +562,71 @@ public final class Choreographer extends Handler { return Looper.myLooper() == mLooper; } + private Callback addCallbackLocked(Callback head, Runnable runnable) { + Callback callback = obtainCallbackLocked(runnable); + if (head == null) { + return callback; + } + Callback tail = head; + while (tail.next != null) { + tail = tail.next; + } + tail.next = callback; + return head; + } + + 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; + } + recycleCallbackLocked(callback); + } else { + predecessor = callback; + } + callback = next; + } + return head; + } + + private void runCallbacks(Callback head) { + while (head != null) { + head.runnable.run(); + head = head.next; + } + } + + 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 void recycleCallbackLocked(Callback callback) { + callback.runnable = null; + callback.next = mCallbackPool; + mCallbackPool = callback; + } + /** * Listens for animation frame timing events. */ @@ -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); @@ -500,4 +681,9 @@ public final class Choreographer extends Handler { doAnimation(); } } + + private static final class Callback { + public Callback next; + public Runnable runnable; + } } |