Loading core/java/android/animation/ValueAnimator.java +1 −1 Original line number Diff line number Diff line Loading @@ -645,7 +645,7 @@ public class ValueAnimator extends Animator { // onAnimate to process the next frame of the animations. if (!mAnimationScheduled && (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty())) { mChoreographer.postAnimationCallback(this); mChoreographer.postAnimationCallback(this, null); mAnimationScheduled = true; } } Loading core/java/android/view/Choreographer.java +185 −97 Original line number Diff line number Diff line Loading @@ -81,8 +81,8 @@ public final class Choreographer { private static final int MSG_DO_ANIMATION = 0; private static final int MSG_DO_DRAW = 1; private static final int MSG_DO_SCHEDULE_VSYNC = 2; private static final int MSG_POST_DELAYED_ANIMATION = 3; private static final int MSG_POST_DELAYED_DRAW = 4; private static final int MSG_DO_SCHEDULE_ANIMATION = 3; private static final int MSG_DO_SCHEDULE_DRAW = 4; private final Object mLock = new Object(); Loading Loading @@ -151,135 +151,159 @@ public final class Choreographer { sFrameDelay = frameDelay; } /** * Subtracts typical frame delay time from a delay interval in milliseconds. * * This method can be used to compensate for animation delay times that have baked * in assumptions about the frame delay. For example, it's quite common for code to * assume a 60Hz frame time and bake in a 16ms delay. When we call * {@link #postAnimationCallbackDelayed} we want to know how long to wait before * posting the animation callback but let the animation timer take care of the remaining * frame delay time. * * This method is somewhat conservative about how much of the frame delay it * subtracts. It uses the same value returned by {@link #getFrameDelay} which by * default is 10ms even though many parts of the system assume 16ms. Consequently, * we might still wait 6ms before posting an animation callback that we want to run * on the next frame, but this is much better than waiting a whole 16ms and likely * missing the deadline. * * @param delayMillis The original delay time including an assumed frame delay. * @return The adjusted delay time with the assumed frame delay subtracted out. */ public static long subtractFrameDelay(long delayMillis) { final long frameDelay = sFrameDelay; return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay; } /** * Posts a callback to run on the next animation cycle. * The callback only runs once and then is automatically removed. * * @param runnable The callback to run during the next animation cycle. * @param action The callback action to run during the next animation cycle. * @param token The callback token, or null if none. * * @see #removeAnimationCallback */ public void postAnimationCallback(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } postAnimationCallbackUnchecked(runnable); } private void postAnimationCallbackUnchecked(Runnable runnable) { synchronized (mLock) { mAnimationCallbacks = addCallbackLocked(mAnimationCallbacks, runnable); scheduleAnimationLocked(); } public void postAnimationCallback(Runnable action, Object token) { postAnimationCallbackDelayed(action, token, 0); } /** * Posts a callback to run on the next animation cycle following the specified delay. * The callback only runs once and then is automatically removed. * * @param runnable The callback to run during the next animation cycle following * @param action The callback action to run during the next animation cycle after * the specified delay. * @param token The callback token, or null if none. * @param delayMillis The delay time in milliseconds. * * @see #removeAnimationCallback */ public void postAnimationCallbackDelayed(Runnable runnable, long delayMillis) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); public void postAnimationCallbackDelayed(Runnable action, Object token, long delayMillis) { if (action == null) { throw new IllegalArgumentException("action must not be null"); } if (delayMillis <= 0) { postAnimationCallbackUnchecked(runnable); synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; mAnimationCallbacks = addCallbackLocked(mAnimationCallbacks, dueTime, action, token); if (dueTime <= now) { scheduleAnimationLocked(now); } else { Message msg = mHandler.obtainMessage(MSG_POST_DELAYED_ANIMATION, runnable); mHandler.sendMessageDelayed(msg, delayMillis); Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_ANIMATION, action); mHandler.sendMessageAtTime(msg, dueTime); } } } /** * Removes animation callbacks for the specified runnable. * Does nothing if the specified animation callback has not been posted or has already * been removed. * Removes animation callbacks that have the specified action and token. * * @param runnable The animation callback to remove. * @param action The action property of the callbacks to remove, or null to remove * callbacks with any action. * @param token The token property of the callbacks to remove, or null to remove * callbacks with any token. * * @see #postAnimationCallback * @see #postAnimationCallbackDelayed */ public void removeAnimationCallbacks(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } public void removeAnimationCallbacks(Runnable action, Object token) { synchronized (mLock) { mAnimationCallbacks = removeCallbacksLocked(mAnimationCallbacks, runnable); mAnimationCallbacks = removeCallbacksLocked(mAnimationCallbacks, action, token); if (action != null && token == null) { mHandler.removeMessages(MSG_DO_SCHEDULE_ANIMATION, action); } } mHandler.removeMessages(MSG_POST_DELAYED_ANIMATION, runnable); } /** * Posts a callback to run on the next draw cycle. * The callback only runs once and then is automatically removed. * * @param runnable The callback to run during the next draw cycle. * @param action The callback action to run during the next draw cycle. * @param token The callback token, or null if none. * * @see #removeDrawCallback */ public void postDrawCallback(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } postDrawCallbackUnchecked(runnable); } private void postDrawCallbackUnchecked(Runnable runnable) { synchronized (mLock) { mDrawCallbacks = addCallbackLocked(mDrawCallbacks, runnable); scheduleDrawLocked(); } public void postDrawCallback(Runnable action, Object token) { postDrawCallbackDelayed(action, token, 0); } /** * Posts a callback to run on the next draw cycle following the specified delay. * The callback only runs once and then is automatically removed. * * @param runnable The callback to run during the next draw cycle following * @param action The callback action to run during the next animation cycle after * the specified delay. * @param token The callback token, or null if none. * @param delayMillis The delay time in milliseconds. * * @see #removeDrawCallback */ public void postDrawCallbackDelayed(Runnable runnable, long delayMillis) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); public void postDrawCallbackDelayed(Runnable action, Object token, long delayMillis) { if (action == null) { throw new IllegalArgumentException("action must not be null"); } if (delayMillis <= 0) { postDrawCallbackUnchecked(runnable); synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; mDrawCallbacks = addCallbackLocked(mDrawCallbacks, dueTime, action, token); scheduleDrawLocked(now); if (dueTime <= now) { scheduleDrawLocked(now); } else { Message msg = mHandler.obtainMessage(MSG_POST_DELAYED_DRAW, runnable); mHandler.sendMessageDelayed(msg, delayMillis); Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_DRAW, action); mHandler.sendMessageAtTime(msg, dueTime); } } } /** * Removes draw callbacks for the specified runnable. * Does nothing if the specified draw callback has not been posted or has already * been removed. * Removes draw callbacks that have the specified action and token. * * @param runnable The draw callback to remove. * @param action The action property of the callbacks to remove, or null to remove * callbacks with any action. * @param token The token property of the callbacks to remove, or null to remove * callbacks with any token. * * @see #postDrawCallback * @see #postDrawCallbackDelayed */ public void removeDrawCallbacks(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } public void removeDrawCallbacks(Runnable action, Object token) { synchronized (mLock) { mDrawCallbacks = removeCallbacksLocked(mDrawCallbacks, runnable); mDrawCallbacks = removeCallbacksLocked(mDrawCallbacks, action, token); if (action != null && token == null) { mHandler.removeMessages(MSG_DO_SCHEDULE_DRAW, action); } } mHandler.removeMessages(MSG_POST_DELAYED_DRAW, runnable); } private void scheduleAnimationLocked() { private void scheduleAnimationLocked(long now) { if (!mAnimationScheduled) { mAnimationScheduled = true; if (USE_VSYNC) { Loading @@ -291,14 +315,13 @@ public final class Choreographer { // otherwise post a message to schedule the vsync from the UI thread // as soon as possible. if (isRunningOnLooperThreadLocked()) { doScheduleVsyncLocked(); scheduleVsyncLocked(); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } } 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."); Loading @@ -310,18 +333,18 @@ public final class Choreographer { } } private void scheduleDrawLocked() { private void scheduleDrawLocked(long now) { if (!mDrawScheduled) { mDrawScheduled = true; if (USE_ANIMATION_TIMER_FOR_DRAW) { scheduleAnimationLocked(); scheduleAnimationLocked(now); } else { if (DEBUG) { Log.d(TAG, "Scheduling draw immediately."); } Message msg = mHandler.obtainMessage(MSG_DO_DRAW); msg.setAsynchronous(true); mHandler.sendMessage(msg); mHandler.sendMessageAtTime(msg, now); } } } Loading @@ -336,7 +359,7 @@ public final class Choreographer { void doAnimationInner() { final long start; final Callback callbacks; Callback callbacks; synchronized (mLock) { if (!mAnimationScheduled) { return; // no work to do Loading @@ -351,7 +374,23 @@ public final class Choreographer { mLastAnimationTime = start; callbacks = mAnimationCallbacks; mAnimationCallbacks = null; if (callbacks != null) { if (callbacks.dueTime > start) { callbacks = null; } else { Callback predecessor = callbacks; Callback successor = predecessor.next; while (successor != null) { if (successor.dueTime > start) { predecessor.next = null; break; } predecessor = successor; successor = successor.next; } mAnimationCallbacks = successor; } } } if (callbacks != null) { Loading @@ -368,7 +407,7 @@ public final class Choreographer { void doDraw() { final long start; final Callback callbacks; Callback callbacks; synchronized (mLock) { if (!mDrawScheduled) { return; // no work to do Loading @@ -383,7 +422,23 @@ public final class Choreographer { mLastDrawTime = start; callbacks = mDrawCallbacks; mDrawCallbacks = null; if (callbacks != null) { if (callbacks.dueTime > start) { callbacks = null; } else { Callback predecessor = callbacks; Callback successor = predecessor.next; while (successor != null) { if (successor.dueTime > start) { predecessor.next = null; break; } predecessor = successor; successor = successor.next; } mDrawCallbacks = successor; } } } if (callbacks != null) { Loading @@ -400,38 +455,66 @@ public final class Choreographer { void doScheduleVsync() { synchronized (mLock) { doScheduleVsyncLocked(); if (mAnimationScheduled) { scheduleVsyncLocked(); } } } private void doScheduleVsyncLocked() { if (mAnimationScheduled) { mDisplayEventReceiver.scheduleVsync(); void doScheduleAnimation() { synchronized (mLock) { final long now = SystemClock.uptimeMillis(); if (mAnimationCallbacks != null && mAnimationCallbacks.dueTime <= now) { scheduleAnimationLocked(now); } } } void doScheduleDraw() { synchronized (mLock) { final long now = SystemClock.uptimeMillis(); if (mDrawCallbacks != null && mDrawCallbacks.dueTime <= now) { scheduleDrawLocked(now); } } } private void scheduleVsyncLocked() { mDisplayEventReceiver.scheduleVsync(); } private boolean isRunningOnLooperThreadLocked() { return Looper.myLooper() == mLooper; } private Callback addCallbackLocked(Callback head, Runnable runnable) { Callback callback = obtainCallbackLocked(runnable); private Callback addCallbackLocked(Callback head, long dueTime, Runnable action, Object token) { Callback callback = obtainCallbackLocked(dueTime, action, token); if (head == null) { return callback; } Callback tail = head; while (tail.next != null) { tail = tail.next; Callback entry = head; if (dueTime < entry.dueTime) { callback.next = entry; return callback; } while (entry.next != null) { if (dueTime < entry.next.dueTime) { callback.next = entry.next; break; } entry = entry.next; } tail.next = callback; entry.next = callback; return head; } private Callback removeCallbacksLocked(Callback head, Runnable runnable) { private Callback removeCallbacksLocked(Callback head, Runnable action, Object token) { Callback predecessor = null; for (Callback callback = head; callback != null;) { final Callback next = callback.next; if (callback.runnable == runnable) { if ((action == null || callback.action == action) && (token == null || callback.token == token)) { if (predecessor != null) { predecessor.next = next; } else { Loading @@ -448,7 +531,7 @@ public final class Choreographer { private void runCallbacks(Callback head) { while (head != null) { head.runnable.run(); head.action.run(); head = head.next; } } Loading @@ -461,7 +544,7 @@ public final class Choreographer { } } private Callback obtainCallbackLocked(Runnable runnable) { private Callback obtainCallbackLocked(long dueTime, Runnable action, Object token) { Callback callback = mCallbackPool; if (callback == null) { callback = new Callback(); Loading @@ -469,12 +552,15 @@ public final class Choreographer { mCallbackPool = callback.next; callback.next = null; } callback.runnable = runnable; callback.dueTime = dueTime; callback.action = action; callback.token = token; return callback; } private void recycleCallbackLocked(Callback callback) { callback.runnable = null; callback.action = null; callback.token = null; callback.next = mCallbackPool; mCallbackPool = callback; } Loading @@ -496,11 +582,11 @@ public final class Choreographer { case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; case MSG_POST_DELAYED_ANIMATION: postAnimationCallbackUnchecked((Runnable)msg.obj); case MSG_DO_SCHEDULE_ANIMATION: doScheduleAnimation(); break; case MSG_POST_DELAYED_DRAW: postDrawCallbackUnchecked((Runnable)msg.obj); case MSG_DO_SCHEDULE_DRAW: doScheduleDraw(); break; } } Loading @@ -519,6 +605,8 @@ public final class Choreographer { private static final class Callback { public Callback next; public Runnable runnable; public long dueTime; public Runnable action; public Object token; } } core/java/android/view/View.java +53 −4 Original line number Diff line number Diff line Loading @@ -8792,6 +8792,52 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal return true; } /** * <p>Causes the Runnable to execute on the next animation time step. * The runnable will be run on the user interface thread.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @param action The Runnable that will be executed. * * @hide */ public void postOnAnimation(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mViewRootImpl.mChoreographer.postAnimationCallback(action, null); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); } } /** * <p>Causes the Runnable to execute on the next animation time step, * after the specified amount of time elapses. * The runnable will be run on the user interface thread.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @param action The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. * * @hide */ public void postOnAnimationDelayed(Runnable action, long delayMillis) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mViewRootImpl.mChoreographer.postAnimationCallbackDelayed( action, null, delayMillis); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); } } /** * <p>Removes the specified Runnable from the message queue.</p> * Loading @@ -8809,6 +8855,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mHandler.removeCallbacks(action); attachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(action, null); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().removeCallbacks(action); Loading Loading @@ -11880,10 +11927,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal */ public void scheduleDrawable(Drawable who, Runnable what, long when) { if (verifyDrawable(who) && what != null) { final long delay = when - SystemClock.uptimeMillis(); if (mAttachInfo != null) { mAttachInfo.mHandler.postAtTime(what, who, when); mAttachInfo.mViewRootImpl.mChoreographer.postAnimationCallbackDelayed( what, who, Choreographer.subtractFrameDelay(delay)); } else { ViewRootImpl.getRunQueue().postDelayed(what, when - SystemClock.uptimeMillis()); ViewRootImpl.getRunQueue().postDelayed(what, delay); } } } Loading @@ -11897,7 +11946,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal public void unscheduleDrawable(Drawable who, Runnable what) { if (verifyDrawable(who) && what != null) { if (mAttachInfo != null) { mAttachInfo.mHandler.removeCallbacks(what, who); mAttachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(what, who); } else { ViewRootImpl.getRunQueue().removeCallbacks(what); } Loading @@ -11915,7 +11964,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal */ public void unscheduleDrawable(Drawable who) { if (mAttachInfo != null) { mAttachInfo.mHandler.removeCallbacksAndMessages(who); mAttachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(null, who); } } Loading core/java/android/view/ViewRootImpl.java +4 −4 Original line number Diff line number Diff line Loading @@ -884,7 +884,7 @@ public final class ViewRootImpl implements ViewParent, void scheduleFrame() { if (!mFrameScheduled) { mFrameScheduled = true; mChoreographer.postDrawCallback(mFrameRunnable); mChoreographer.postDrawCallback(mFrameRunnable, null); } } Loading @@ -893,7 +893,7 @@ public final class ViewRootImpl implements ViewParent, if (mFrameScheduled) { mFrameScheduled = false; mChoreographer.removeDrawCallbacks(mFrameRunnable); mChoreographer.removeDrawCallbacks(mFrameRunnable, null); } } Loading Loading @@ -4051,7 +4051,7 @@ public final class ViewRootImpl implements ViewParent, } if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) { mChoreographer.removeAnimationCallbacks(this); mChoreographer.removeAnimationCallbacks(this, null); mPosted = false; } } Loading Loading @@ -4092,7 +4092,7 @@ public final class ViewRootImpl implements ViewParent, private void postIfNeededLocked() { if (!mPosted) { mChoreographer.postAnimationCallback(this); mChoreographer.postAnimationCallback(this, null); mPosted = true; } } Loading services/java/com/android/server/wm/WindowManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -9173,7 +9173,7 @@ public class WindowManagerService extends IWindowManager.Stub void scheduleAnimationLocked() { if (!mAnimationScheduled) { mChoreographer.postAnimationCallback(mAnimationRunnable); mChoreographer.postAnimationCallback(mAnimationRunnable, null); mAnimationScheduled = true; } } Loading Loading
core/java/android/animation/ValueAnimator.java +1 −1 Original line number Diff line number Diff line Loading @@ -645,7 +645,7 @@ public class ValueAnimator extends Animator { // onAnimate to process the next frame of the animations. if (!mAnimationScheduled && (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty())) { mChoreographer.postAnimationCallback(this); mChoreographer.postAnimationCallback(this, null); mAnimationScheduled = true; } } Loading
core/java/android/view/Choreographer.java +185 −97 Original line number Diff line number Diff line Loading @@ -81,8 +81,8 @@ public final class Choreographer { private static final int MSG_DO_ANIMATION = 0; private static final int MSG_DO_DRAW = 1; private static final int MSG_DO_SCHEDULE_VSYNC = 2; private static final int MSG_POST_DELAYED_ANIMATION = 3; private static final int MSG_POST_DELAYED_DRAW = 4; private static final int MSG_DO_SCHEDULE_ANIMATION = 3; private static final int MSG_DO_SCHEDULE_DRAW = 4; private final Object mLock = new Object(); Loading Loading @@ -151,135 +151,159 @@ public final class Choreographer { sFrameDelay = frameDelay; } /** * Subtracts typical frame delay time from a delay interval in milliseconds. * * This method can be used to compensate for animation delay times that have baked * in assumptions about the frame delay. For example, it's quite common for code to * assume a 60Hz frame time and bake in a 16ms delay. When we call * {@link #postAnimationCallbackDelayed} we want to know how long to wait before * posting the animation callback but let the animation timer take care of the remaining * frame delay time. * * This method is somewhat conservative about how much of the frame delay it * subtracts. It uses the same value returned by {@link #getFrameDelay} which by * default is 10ms even though many parts of the system assume 16ms. Consequently, * we might still wait 6ms before posting an animation callback that we want to run * on the next frame, but this is much better than waiting a whole 16ms and likely * missing the deadline. * * @param delayMillis The original delay time including an assumed frame delay. * @return The adjusted delay time with the assumed frame delay subtracted out. */ public static long subtractFrameDelay(long delayMillis) { final long frameDelay = sFrameDelay; return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay; } /** * Posts a callback to run on the next animation cycle. * The callback only runs once and then is automatically removed. * * @param runnable The callback to run during the next animation cycle. * @param action The callback action to run during the next animation cycle. * @param token The callback token, or null if none. * * @see #removeAnimationCallback */ public void postAnimationCallback(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } postAnimationCallbackUnchecked(runnable); } private void postAnimationCallbackUnchecked(Runnable runnable) { synchronized (mLock) { mAnimationCallbacks = addCallbackLocked(mAnimationCallbacks, runnable); scheduleAnimationLocked(); } public void postAnimationCallback(Runnable action, Object token) { postAnimationCallbackDelayed(action, token, 0); } /** * Posts a callback to run on the next animation cycle following the specified delay. * The callback only runs once and then is automatically removed. * * @param runnable The callback to run during the next animation cycle following * @param action The callback action to run during the next animation cycle after * the specified delay. * @param token The callback token, or null if none. * @param delayMillis The delay time in milliseconds. * * @see #removeAnimationCallback */ public void postAnimationCallbackDelayed(Runnable runnable, long delayMillis) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); public void postAnimationCallbackDelayed(Runnable action, Object token, long delayMillis) { if (action == null) { throw new IllegalArgumentException("action must not be null"); } if (delayMillis <= 0) { postAnimationCallbackUnchecked(runnable); synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; mAnimationCallbacks = addCallbackLocked(mAnimationCallbacks, dueTime, action, token); if (dueTime <= now) { scheduleAnimationLocked(now); } else { Message msg = mHandler.obtainMessage(MSG_POST_DELAYED_ANIMATION, runnable); mHandler.sendMessageDelayed(msg, delayMillis); Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_ANIMATION, action); mHandler.sendMessageAtTime(msg, dueTime); } } } /** * Removes animation callbacks for the specified runnable. * Does nothing if the specified animation callback has not been posted or has already * been removed. * Removes animation callbacks that have the specified action and token. * * @param runnable The animation callback to remove. * @param action The action property of the callbacks to remove, or null to remove * callbacks with any action. * @param token The token property of the callbacks to remove, or null to remove * callbacks with any token. * * @see #postAnimationCallback * @see #postAnimationCallbackDelayed */ public void removeAnimationCallbacks(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } public void removeAnimationCallbacks(Runnable action, Object token) { synchronized (mLock) { mAnimationCallbacks = removeCallbacksLocked(mAnimationCallbacks, runnable); mAnimationCallbacks = removeCallbacksLocked(mAnimationCallbacks, action, token); if (action != null && token == null) { mHandler.removeMessages(MSG_DO_SCHEDULE_ANIMATION, action); } } mHandler.removeMessages(MSG_POST_DELAYED_ANIMATION, runnable); } /** * Posts a callback to run on the next draw cycle. * The callback only runs once and then is automatically removed. * * @param runnable The callback to run during the next draw cycle. * @param action The callback action to run during the next draw cycle. * @param token The callback token, or null if none. * * @see #removeDrawCallback */ public void postDrawCallback(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } postDrawCallbackUnchecked(runnable); } private void postDrawCallbackUnchecked(Runnable runnable) { synchronized (mLock) { mDrawCallbacks = addCallbackLocked(mDrawCallbacks, runnable); scheduleDrawLocked(); } public void postDrawCallback(Runnable action, Object token) { postDrawCallbackDelayed(action, token, 0); } /** * Posts a callback to run on the next draw cycle following the specified delay. * The callback only runs once and then is automatically removed. * * @param runnable The callback to run during the next draw cycle following * @param action The callback action to run during the next animation cycle after * the specified delay. * @param token The callback token, or null if none. * @param delayMillis The delay time in milliseconds. * * @see #removeDrawCallback */ public void postDrawCallbackDelayed(Runnable runnable, long delayMillis) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); public void postDrawCallbackDelayed(Runnable action, Object token, long delayMillis) { if (action == null) { throw new IllegalArgumentException("action must not be null"); } if (delayMillis <= 0) { postDrawCallbackUnchecked(runnable); synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; mDrawCallbacks = addCallbackLocked(mDrawCallbacks, dueTime, action, token); scheduleDrawLocked(now); if (dueTime <= now) { scheduleDrawLocked(now); } else { Message msg = mHandler.obtainMessage(MSG_POST_DELAYED_DRAW, runnable); mHandler.sendMessageDelayed(msg, delayMillis); Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_DRAW, action); mHandler.sendMessageAtTime(msg, dueTime); } } } /** * Removes draw callbacks for the specified runnable. * Does nothing if the specified draw callback has not been posted or has already * been removed. * Removes draw callbacks that have the specified action and token. * * @param runnable The draw callback to remove. * @param action The action property of the callbacks to remove, or null to remove * callbacks with any action. * @param token The token property of the callbacks to remove, or null to remove * callbacks with any token. * * @see #postDrawCallback * @see #postDrawCallbackDelayed */ public void removeDrawCallbacks(Runnable runnable) { if (runnable == null) { throw new IllegalArgumentException("runnable must not be null"); } public void removeDrawCallbacks(Runnable action, Object token) { synchronized (mLock) { mDrawCallbacks = removeCallbacksLocked(mDrawCallbacks, runnable); mDrawCallbacks = removeCallbacksLocked(mDrawCallbacks, action, token); if (action != null && token == null) { mHandler.removeMessages(MSG_DO_SCHEDULE_DRAW, action); } } mHandler.removeMessages(MSG_POST_DELAYED_DRAW, runnable); } private void scheduleAnimationLocked() { private void scheduleAnimationLocked(long now) { if (!mAnimationScheduled) { mAnimationScheduled = true; if (USE_VSYNC) { Loading @@ -291,14 +315,13 @@ public final class Choreographer { // otherwise post a message to schedule the vsync from the UI thread // as soon as possible. if (isRunningOnLooperThreadLocked()) { doScheduleVsyncLocked(); scheduleVsyncLocked(); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } } 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."); Loading @@ -310,18 +333,18 @@ public final class Choreographer { } } private void scheduleDrawLocked() { private void scheduleDrawLocked(long now) { if (!mDrawScheduled) { mDrawScheduled = true; if (USE_ANIMATION_TIMER_FOR_DRAW) { scheduleAnimationLocked(); scheduleAnimationLocked(now); } else { if (DEBUG) { Log.d(TAG, "Scheduling draw immediately."); } Message msg = mHandler.obtainMessage(MSG_DO_DRAW); msg.setAsynchronous(true); mHandler.sendMessage(msg); mHandler.sendMessageAtTime(msg, now); } } } Loading @@ -336,7 +359,7 @@ public final class Choreographer { void doAnimationInner() { final long start; final Callback callbacks; Callback callbacks; synchronized (mLock) { if (!mAnimationScheduled) { return; // no work to do Loading @@ -351,7 +374,23 @@ public final class Choreographer { mLastAnimationTime = start; callbacks = mAnimationCallbacks; mAnimationCallbacks = null; if (callbacks != null) { if (callbacks.dueTime > start) { callbacks = null; } else { Callback predecessor = callbacks; Callback successor = predecessor.next; while (successor != null) { if (successor.dueTime > start) { predecessor.next = null; break; } predecessor = successor; successor = successor.next; } mAnimationCallbacks = successor; } } } if (callbacks != null) { Loading @@ -368,7 +407,7 @@ public final class Choreographer { void doDraw() { final long start; final Callback callbacks; Callback callbacks; synchronized (mLock) { if (!mDrawScheduled) { return; // no work to do Loading @@ -383,7 +422,23 @@ public final class Choreographer { mLastDrawTime = start; callbacks = mDrawCallbacks; mDrawCallbacks = null; if (callbacks != null) { if (callbacks.dueTime > start) { callbacks = null; } else { Callback predecessor = callbacks; Callback successor = predecessor.next; while (successor != null) { if (successor.dueTime > start) { predecessor.next = null; break; } predecessor = successor; successor = successor.next; } mDrawCallbacks = successor; } } } if (callbacks != null) { Loading @@ -400,38 +455,66 @@ public final class Choreographer { void doScheduleVsync() { synchronized (mLock) { doScheduleVsyncLocked(); if (mAnimationScheduled) { scheduleVsyncLocked(); } } } private void doScheduleVsyncLocked() { if (mAnimationScheduled) { mDisplayEventReceiver.scheduleVsync(); void doScheduleAnimation() { synchronized (mLock) { final long now = SystemClock.uptimeMillis(); if (mAnimationCallbacks != null && mAnimationCallbacks.dueTime <= now) { scheduleAnimationLocked(now); } } } void doScheduleDraw() { synchronized (mLock) { final long now = SystemClock.uptimeMillis(); if (mDrawCallbacks != null && mDrawCallbacks.dueTime <= now) { scheduleDrawLocked(now); } } } private void scheduleVsyncLocked() { mDisplayEventReceiver.scheduleVsync(); } private boolean isRunningOnLooperThreadLocked() { return Looper.myLooper() == mLooper; } private Callback addCallbackLocked(Callback head, Runnable runnable) { Callback callback = obtainCallbackLocked(runnable); private Callback addCallbackLocked(Callback head, long dueTime, Runnable action, Object token) { Callback callback = obtainCallbackLocked(dueTime, action, token); if (head == null) { return callback; } Callback tail = head; while (tail.next != null) { tail = tail.next; Callback entry = head; if (dueTime < entry.dueTime) { callback.next = entry; return callback; } while (entry.next != null) { if (dueTime < entry.next.dueTime) { callback.next = entry.next; break; } entry = entry.next; } tail.next = callback; entry.next = callback; return head; } private Callback removeCallbacksLocked(Callback head, Runnable runnable) { private Callback removeCallbacksLocked(Callback head, Runnable action, Object token) { Callback predecessor = null; for (Callback callback = head; callback != null;) { final Callback next = callback.next; if (callback.runnable == runnable) { if ((action == null || callback.action == action) && (token == null || callback.token == token)) { if (predecessor != null) { predecessor.next = next; } else { Loading @@ -448,7 +531,7 @@ public final class Choreographer { private void runCallbacks(Callback head) { while (head != null) { head.runnable.run(); head.action.run(); head = head.next; } } Loading @@ -461,7 +544,7 @@ public final class Choreographer { } } private Callback obtainCallbackLocked(Runnable runnable) { private Callback obtainCallbackLocked(long dueTime, Runnable action, Object token) { Callback callback = mCallbackPool; if (callback == null) { callback = new Callback(); Loading @@ -469,12 +552,15 @@ public final class Choreographer { mCallbackPool = callback.next; callback.next = null; } callback.runnable = runnable; callback.dueTime = dueTime; callback.action = action; callback.token = token; return callback; } private void recycleCallbackLocked(Callback callback) { callback.runnable = null; callback.action = null; callback.token = null; callback.next = mCallbackPool; mCallbackPool = callback; } Loading @@ -496,11 +582,11 @@ public final class Choreographer { case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; case MSG_POST_DELAYED_ANIMATION: postAnimationCallbackUnchecked((Runnable)msg.obj); case MSG_DO_SCHEDULE_ANIMATION: doScheduleAnimation(); break; case MSG_POST_DELAYED_DRAW: postDrawCallbackUnchecked((Runnable)msg.obj); case MSG_DO_SCHEDULE_DRAW: doScheduleDraw(); break; } } Loading @@ -519,6 +605,8 @@ public final class Choreographer { private static final class Callback { public Callback next; public Runnable runnable; public long dueTime; public Runnable action; public Object token; } }
core/java/android/view/View.java +53 −4 Original line number Diff line number Diff line Loading @@ -8792,6 +8792,52 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal return true; } /** * <p>Causes the Runnable to execute on the next animation time step. * The runnable will be run on the user interface thread.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @param action The Runnable that will be executed. * * @hide */ public void postOnAnimation(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mViewRootImpl.mChoreographer.postAnimationCallback(action, null); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); } } /** * <p>Causes the Runnable to execute on the next animation time step, * after the specified amount of time elapses. * The runnable will be run on the user interface thread.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @param action The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. * * @hide */ public void postOnAnimationDelayed(Runnable action, long delayMillis) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mViewRootImpl.mChoreographer.postAnimationCallbackDelayed( action, null, delayMillis); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); } } /** * <p>Removes the specified Runnable from the message queue.</p> * Loading @@ -8809,6 +8855,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mHandler.removeCallbacks(action); attachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(action, null); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().removeCallbacks(action); Loading Loading @@ -11880,10 +11927,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal */ public void scheduleDrawable(Drawable who, Runnable what, long when) { if (verifyDrawable(who) && what != null) { final long delay = when - SystemClock.uptimeMillis(); if (mAttachInfo != null) { mAttachInfo.mHandler.postAtTime(what, who, when); mAttachInfo.mViewRootImpl.mChoreographer.postAnimationCallbackDelayed( what, who, Choreographer.subtractFrameDelay(delay)); } else { ViewRootImpl.getRunQueue().postDelayed(what, when - SystemClock.uptimeMillis()); ViewRootImpl.getRunQueue().postDelayed(what, delay); } } } Loading @@ -11897,7 +11946,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal public void unscheduleDrawable(Drawable who, Runnable what) { if (verifyDrawable(who) && what != null) { if (mAttachInfo != null) { mAttachInfo.mHandler.removeCallbacks(what, who); mAttachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(what, who); } else { ViewRootImpl.getRunQueue().removeCallbacks(what); } Loading @@ -11915,7 +11964,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal */ public void unscheduleDrawable(Drawable who) { if (mAttachInfo != null) { mAttachInfo.mHandler.removeCallbacksAndMessages(who); mAttachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(null, who); } } Loading
core/java/android/view/ViewRootImpl.java +4 −4 Original line number Diff line number Diff line Loading @@ -884,7 +884,7 @@ public final class ViewRootImpl implements ViewParent, void scheduleFrame() { if (!mFrameScheduled) { mFrameScheduled = true; mChoreographer.postDrawCallback(mFrameRunnable); mChoreographer.postDrawCallback(mFrameRunnable, null); } } Loading @@ -893,7 +893,7 @@ public final class ViewRootImpl implements ViewParent, if (mFrameScheduled) { mFrameScheduled = false; mChoreographer.removeDrawCallbacks(mFrameRunnable); mChoreographer.removeDrawCallbacks(mFrameRunnable, null); } } Loading Loading @@ -4051,7 +4051,7 @@ public final class ViewRootImpl implements ViewParent, } if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) { mChoreographer.removeAnimationCallbacks(this); mChoreographer.removeAnimationCallbacks(this, null); mPosted = false; } } Loading Loading @@ -4092,7 +4092,7 @@ public final class ViewRootImpl implements ViewParent, private void postIfNeededLocked() { if (!mPosted) { mChoreographer.postAnimationCallback(this); mChoreographer.postAnimationCallback(this, null); mPosted = true; } } Loading
services/java/com/android/server/wm/WindowManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -9173,7 +9173,7 @@ public class WindowManagerService extends IWindowManager.Stub void scheduleAnimationLocked() { if (!mAnimationScheduled) { mChoreographer.postAnimationCallback(mAnimationRunnable); mChoreographer.postAnimationCallback(mAnimationRunnable, null); mAnimationScheduled = true; } } Loading