summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jeff Brown <jeffbrown@google.com> 2012-02-14 19:41:32 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2012-02-14 19:41:32 -0800
commitfef3d62b16bccd9ef7a32c2e1da94f694b34c405 (patch)
tree56a65ff3179b267310a904a12d5ca9efbe0e1b8a
parent472ea60685c35b450ecf380d8946f10835c210cc (diff)
parent968588573cf516fd8cf33c133d06f06c67ca1785 (diff)
Merge "Add support for posting Runnables to the Choreographer."
-rw-r--r--core/java/android/view/Choreographer.java438
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;
+ }
}