diff options
author | Jeff Brown <jeffbrown@google.com> | 2012-03-23 17:14:34 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2012-03-29 13:10:55 -0700 |
commit | ebb2d8d708c5c58c79ae88ac2bd10450a856f702 (patch) | |
tree | 7ea7f937f0a4b172ade6a5d310041fe5b6893672 /core/java | |
parent | 70825161b5bf51ed48319e142751a9c88b104994 (diff) | |
download | frameworks_base-ebb2d8d708c5c58c79ae88ac2bd10450a856f702.zip frameworks_base-ebb2d8d708c5c58c79ae88ac2bd10450a856f702.tar.gz frameworks_base-ebb2d8d708c5c58c79ae88ac2bd10450a856f702.tar.bz2 |
Enable vsync traversals by default.
Improved how the various callbacks are managed and sequenced
to reduce code duplication.
Added a heuristic to avoid postponing traversals until
the next vsync frame if we did not actually do any drawing during
the previous frame. This helps in the very common case where
drawing occurs in response to input.
Change-Id: I277d9eeaf50408f8745a3cfd181db1d140770658
Diffstat (limited to 'core/java')
-rwxr-xr-x | core/java/android/animation/ValueAnimator.java | 2 | ||||
-rw-r--r-- | core/java/android/view/Choreographer.java | 380 | ||||
-rw-r--r-- | core/java/android/view/View.java | 21 | ||||
-rw-r--r-- | core/java/android/view/ViewRootImpl.java | 198 |
4 files changed, 294 insertions, 307 deletions
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index e2b8ce4..fade20c 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -647,7 +647,7 @@ public class ValueAnimator extends Animator { // onAnimate to process the next frame of the animations. if (!mAnimationScheduled && (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty())) { - mChoreographer.postAnimationCallback(this, null); + mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); mAnimationScheduled = true; } } diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index d217cab..1cb15a6 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -65,24 +65,22 @@ public final class Choreographer { } }; - // System property to enable/disable vsync for animations and drawing. - // Enabled by default. + // Enable/disable vsync for animations and drawing. private static final boolean USE_VSYNC = SystemProperties.getBoolean( "debug.choreographer.vsync", true); - // System property to enable/disable the use of the vsync / animation timer - // for drawing rather than drawing immediately. - // Temporarily disabled by default because postponing performTraversals() violates - // assumptions about traversals happening in-order relative to other posted messages. - // Bug: 5721047 - private static final boolean USE_ANIMATION_TIMER_FOR_DRAW = SystemProperties.getBoolean( - "debug.choreographer.animdraw", false); + // Enable/disable allowing traversals to proceed immediately if no drawing occurred + // during the previous frame. When true, the Choreographer can degrade more gracefully + // if drawing takes longer than a frame, but it may potentially block in eglSwapBuffers() + // if there are two dirty buffers enqueued. + // When false, we always schedule traversals on strict vsync boundaries. + private static final boolean USE_PIPELINING = SystemProperties.getBoolean( + "debug.choreographer.pipeline", false); - 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_DO_SCHEDULE_ANIMATION = 3; - private static final int MSG_DO_SCHEDULE_DRAW = 4; + private static final int MSG_DO_FRAME = 0; + private static final int MSG_DO_SCHEDULE_VSYNC = 1; + private static final int MSG_DO_SCHEDULE_CALLBACK = 2; + private static final int MSG_DO_TRAVERSAL = 3; private final Object mLock = new Object(); @@ -92,20 +90,41 @@ public final class Choreographer { private Callback mCallbackPool; - private final CallbackQueue mAnimationCallbackQueue = new CallbackQueue(); - private final CallbackQueue mDrawCallbackQueue = new CallbackQueue(); + private final CallbackQueue[] mCallbackQueues; - private boolean mAnimationScheduled; - private boolean mDrawScheduled; - private long mLastAnimationTime; - private long mLastDrawTime; + private boolean mFrameScheduled; + private long mLastFrameTime; + private boolean mDrewLastFrame; + private boolean mTraversalScheduled; + + /** + * Callback type: Input callback. Runs first. + */ + public static final int CALLBACK_INPUT = 0; + + /** + * Callback type: Animation callback. Runs before traversals. + */ + public static final int CALLBACK_ANIMATION = 1; + + /** + * Callback type: Traversal callback. Handles layout and draw. Runs last + * after all other asynchronous messages have been handled. + */ + public static final int CALLBACK_TRAVERSAL = 2; + + private static final int CALLBACK_LAST = CALLBACK_TRAVERSAL; private Choreographer(Looper looper) { mLooper = looper; mHandler = new FrameHandler(looper); mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null; - mLastAnimationTime = Long.MIN_VALUE; - mLastDrawTime = Long.MIN_VALUE; + mLastFrameTime = Long.MIN_VALUE; + + mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; + for (int i = 0; i <= CALLBACK_LAST; i++) { + mCallbackQueues[i] = new CallbackQueue(); + } } /** @@ -177,156 +196,142 @@ public final class Choreographer { } /** - * Posts a callback to run on the next animation cycle. + * Posts a callback to run on the next frame. * The callback only runs once and then is automatically removed. * - * @param action The callback action to run during the next animation cycle. + * @param callbackType The callback type. + * @param action The callback action to run during the next frame. * @param token The callback token, or null if none. * - * @see #removeAnimationCallback + * @see #removeCallbacks */ - public void postAnimationCallback(Runnable action, Object token) { - postAnimationCallbackDelayed(action, token, 0); + public void postCallback(int callbackType, Runnable action, Object token) { + postCallbackDelayed(callbackType, action, token, 0); } /** - * Posts a callback to run on the next animation cycle following the specified delay. + * Posts a callback to run on the next frame following the specified delay. * The callback only runs once and then is automatically removed. * - * @param action The callback action to run during the next animation cycle after - * the specified delay. + * @param callbackType The callback type. + * @param action The callback action to run during the next frame after the specified delay. * @param token The callback token, or null if none. * @param delayMillis The delay time in milliseconds. * - * @see #removeAnimationCallback + * @see #removeCallback */ - public void postAnimationCallbackDelayed(Runnable action, Object token, long delayMillis) { + public void postCallbackDelayed(int callbackType, + Runnable action, Object token, long delayMillis) { if (action == null) { throw new IllegalArgumentException("action must not be null"); } + if (callbackType < 0 || callbackType > CALLBACK_LAST) { + throw new IllegalArgumentException("callbackType is invalid"); + } if (DEBUG) { - Log.d(TAG, "PostAnimationCallback: " + action + ", token=" + token + Log.d(TAG, "PostCallback: type=" + callbackType + + ", action=" + action + ", token=" + token + ", delayMillis=" + delayMillis); } synchronized (mLock) { + if (USE_PIPELINING && callbackType == CALLBACK_INPUT) { + Message msg = Message.obtain(mHandler, action); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + return; + } + final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; - mAnimationCallbackQueue.addCallbackLocked(dueTime, action, token); + mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); if (dueTime <= now) { - scheduleAnimationLocked(now); + if (USE_PIPELINING && callbackType == CALLBACK_TRAVERSAL) { + if (!mDrewLastFrame) { + if (DEBUG) { + Log.d(TAG, "Scheduling traversal immediately."); + } + if (!mTraversalScheduled) { + mTraversalScheduled = true; + Message msg = mHandler.obtainMessage(MSG_DO_TRAVERSAL); + msg.setAsynchronous(true); + mHandler.sendMessageAtTime(msg, dueTime); + } + return; + } + if (DEBUG) { + Log.d(TAG, "Scheduling traversal on next frame."); + } + } + scheduleFrameLocked(now); } else { - Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_ANIMATION, action); + Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); + msg.arg1 = callbackType; + msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, dueTime); } } } /** - * Removes animation callbacks that have the specified action and token. + * Removes callbacks that have the specified action and token. * + * @param callbackType The callback type. * @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 + * @see #postCallback + * @see #postCallbackDelayed */ - public void removeAnimationCallbacks(Runnable action, Object token) { - if (DEBUG) { - Log.d(TAG, "RemoveAnimationCallbacks: " + action + ", token=" + token); - } - - synchronized (mLock) { - mAnimationCallbackQueue.removeCallbacksLocked(action, token); - if (action != null && token == null) { - mHandler.removeMessages(MSG_DO_SCHEDULE_ANIMATION, action); - } - } - } - - /** - * Posts a callback to run on the next draw cycle. - * The callback only runs once and then is automatically removed. - * - * @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 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 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 action, Object token, long delayMillis) { - if (action == null) { - throw new IllegalArgumentException("action must not be null"); + public void removeCallbacks(int callbackType, Runnable action, Object token) { + if (callbackType < 0 || callbackType > CALLBACK_LAST) { + throw new IllegalArgumentException("callbackType is invalid"); } if (DEBUG) { - Log.d(TAG, "PostDrawCallback: " + action + ", token=" + token - + ", delayMillis=" + delayMillis); + Log.d(TAG, "RemoveCallbacks: type=" + callbackType + + ", action=" + action + ", token=" + token); } synchronized (mLock) { - final long now = SystemClock.uptimeMillis(); - final long dueTime = now + delayMillis; - mDrawCallbackQueue.addCallbackLocked(dueTime, action, token); - scheduleDrawLocked(now); - - if (dueTime <= now) { - scheduleDrawLocked(now); - } else { - Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_DRAW, action); - mHandler.sendMessageAtTime(msg, dueTime); + mCallbackQueues[callbackType].removeCallbacksLocked(action, token); + if (action != null && token == null) { + mHandler.removeMessages(MSG_DO_SCHEDULE_CALLBACK, action); } } } /** - * Removes draw callbacks that have the specified action and token. + * Tells the choreographer that the application has actually drawn to a surface. * - * @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 + * It uses this information to determine whether to draw immediately or to + * post a draw to the next vsync because it might otherwise block. */ - public void removeDrawCallbacks(Runnable action, Object token) { + public void notifyDrawOccurred() { if (DEBUG) { - Log.d(TAG, "RemoveDrawCallbacks: " + action + ", token=" + token); + Log.d(TAG, "Draw occurred."); } - synchronized (mLock) { - mDrawCallbackQueue.removeCallbacksLocked(action, token); - if (action != null && token == null) { - mHandler.removeMessages(MSG_DO_SCHEDULE_DRAW, action); + if (USE_PIPELINING) { + synchronized (mLock) { + if (!mDrewLastFrame) { + mDrewLastFrame = true; + scheduleFrameLocked(SystemClock.uptimeMillis()); + } } } } - private void scheduleAnimationLocked(long now) { - if (!mAnimationScheduled) { - mAnimationScheduled = true; + private void scheduleFrameLocked(long now) { + if (!mFrameScheduled) { + mFrameScheduled = true; if (USE_VSYNC) { if (DEBUG) { - Log.d(TAG, "Scheduling vsync for animation."); + Log.d(TAG, "Scheduling next frame on vsync."); } // If running on the Looper thread, then schedule the vsync immediately, @@ -340,125 +345,94 @@ public final class Choreographer { mHandler.sendMessageAtFrontOfQueue(msg); } } else { - final long nextAnimationTime = Math.max(mLastAnimationTime + sFrameDelay, now); + final long nextFrameTime = Math.max(mLastFrameTime + sFrameDelay, now); if (DEBUG) { - Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms."); + Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms."); } - Message msg = mHandler.obtainMessage(MSG_DO_ANIMATION); + Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true); - mHandler.sendMessageAtTime(msg, nextAnimationTime); + mHandler.sendMessageAtTime(msg, nextFrameTime); } } } - private void scheduleDrawLocked(long now) { - if (!mDrawScheduled) { - mDrawScheduled = true; - if (USE_ANIMATION_TIMER_FOR_DRAW) { - scheduleAnimationLocked(now); - } else { - if (DEBUG) { - Log.d(TAG, "Scheduling draw immediately."); - } - Message msg = mHandler.obtainMessage(MSG_DO_DRAW); - msg.setAsynchronous(true); - mHandler.sendMessageAtTime(msg, now); - } - } - } - - void doAnimation() { - doAnimationInner(); - - if (USE_ANIMATION_TIMER_FOR_DRAW) { - doDraw(); - } - } - - void doAnimationInner() { - final long start; - Callback callbacks; + void doFrame(int frame) { synchronized (mLock) { - if (!mAnimationScheduled) { + if (!mFrameScheduled) { return; // no work to do } - mAnimationScheduled = false; - - start = SystemClock.uptimeMillis(); - if (DEBUG) { - Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime) - + " ms have elapsed since previous animation."); - } - mLastAnimationTime = start; - - callbacks = mAnimationCallbackQueue.extractDueCallbacksLocked(start); + mFrameScheduled = false; + mLastFrameTime = SystemClock.uptimeMillis(); + mDrewLastFrame = false; } - if (callbacks != null) { - runCallbacks(callbacks); - synchronized (mLock) { - recycleCallbacksLocked(callbacks); - } - } + doCallbacks(Choreographer.CALLBACK_INPUT); + doCallbacks(Choreographer.CALLBACK_ANIMATION); + doCallbacks(Choreographer.CALLBACK_TRAVERSAL); if (DEBUG) { - Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms."); + Log.d(TAG, "Frame " + frame + ": Finished, took " + + (SystemClock.uptimeMillis() - mLastFrameTime) + " ms."); } } - void doDraw() { + void doCallbacks(int callbackType) { final long start; Callback callbacks; synchronized (mLock) { - if (!mDrawScheduled) { - return; // no work to do - } - mDrawScheduled = false; - start = SystemClock.uptimeMillis(); - if (DEBUG) { - Log.d(TAG, "Performing draw: " + Math.max(0, start - mLastDrawTime) - + " ms have elapsed since previous draw."); - } - mLastDrawTime = start; + callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(start); - callbacks = mDrawCallbackQueue.extractDueCallbacksLocked(start); + if (USE_PIPELINING && callbackType == CALLBACK_TRAVERSAL && mTraversalScheduled) { + mTraversalScheduled = false; + mHandler.removeMessages(MSG_DO_TRAVERSAL); + } } if (callbacks != null) { - runCallbacks(callbacks); - synchronized (mLock) { - recycleCallbacksLocked(callbacks); + for (Callback c = callbacks; c != null; c = c.next) { + if (DEBUG) { + Log.d(TAG, "RunCallback: type=" + callbackType + + ", action=" + c.action + ", token=" + c.token + + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime)); + } + c.action.run(); } - } - if (DEBUG) { - Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms."); + synchronized (mLock) { + do { + final Callback next = callbacks.next; + recycleCallbackLocked(callbacks); + callbacks = next; + } while (callbacks != null); + } } } void doScheduleVsync() { synchronized (mLock) { - if (mAnimationScheduled) { + if (mFrameScheduled) { scheduleVsyncLocked(); } } } - void doScheduleAnimation() { + void doScheduleCallback(int callbackType) { synchronized (mLock) { - final long now = SystemClock.uptimeMillis(); - if (mAnimationCallbackQueue.hasDueCallbacksLocked(now)) { - scheduleAnimationLocked(now); + if (!mFrameScheduled) { + final long now = SystemClock.uptimeMillis(); + if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) { + scheduleFrameLocked(now); + } } } } - void doScheduleDraw() { + void doTraversal() { synchronized (mLock) { - final long now = SystemClock.uptimeMillis(); - if (mDrawCallbackQueue.hasDueCallbacksLocked(now)) { - scheduleDrawLocked(now); + if (mTraversalScheduled) { + mTraversalScheduled = false; + doCallbacks(CALLBACK_TRAVERSAL); } } } @@ -471,25 +445,6 @@ public final class Choreographer { return Looper.myLooper() == mLooper; } - private void runCallbacks(Callback head) { - while (head != null) { - if (DEBUG) { - Log.d(TAG, "RunCallback: " + head.action + ", token=" + head.token - + ", waitMillis=" + (SystemClock.uptimeMillis() - head.dueTime)); - } - head.action.run(); - head = head.next; - } - } - - private void recycleCallbacksLocked(Callback head) { - while (head != null) { - final Callback next = head.next; - recycleCallbackLocked(head); - head = next; - } - } - private Callback obtainCallbackLocked(long dueTime, Runnable action, Object token) { Callback callback = mCallbackPool; if (callback == null) { @@ -519,20 +474,17 @@ public final class Choreographer { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_DO_ANIMATION: - doAnimation(); - break; - case MSG_DO_DRAW: - doDraw(); + case MSG_DO_FRAME: + doFrame(0); break; case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; - case MSG_DO_SCHEDULE_ANIMATION: - doScheduleAnimation(); + case MSG_DO_SCHEDULE_CALLBACK: + doScheduleCallback(msg.arg1); break; - case MSG_DO_SCHEDULE_DRAW: - doScheduleDraw(); + case MSG_DO_TRAVERSAL: + doTraversal(); break; } } @@ -545,7 +497,7 @@ public final class Choreographer { @Override public void onVsync(long timestampNanos, int frame) { - doAnimation(); + doFrame(frame); } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 770d899..2ab27fc 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -8981,7 +8981,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal public void postOnAnimation(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { - attachInfo.mViewRootImpl.mChoreographer.postAnimationCallback(action, null); + attachInfo.mViewRootImpl.mChoreographer.postCallback( + Choreographer.CALLBACK_ANIMATION, action, null); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); @@ -9005,8 +9006,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal public void postOnAnimationDelayed(Runnable action, long delayMillis) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { - attachInfo.mViewRootImpl.mChoreographer.postAnimationCallbackDelayed( - action, null, delayMillis); + attachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed( + Choreographer.CALLBACK_ANIMATION, action, null, delayMillis); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); @@ -9031,7 +9032,8 @@ 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); + attachInfo.mViewRootImpl.mChoreographer.removeCallbacks( + Choreographer.CALLBACK_ANIMATION, action, null); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().removeCallbacks(action); @@ -12226,8 +12228,9 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal if (verifyDrawable(who) && what != null) { final long delay = when - SystemClock.uptimeMillis(); if (mAttachInfo != null) { - mAttachInfo.mViewRootImpl.mChoreographer.postAnimationCallbackDelayed( - what, who, Choreographer.subtractFrameDelay(delay)); + mAttachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed( + Choreographer.CALLBACK_ANIMATION, what, who, + Choreographer.subtractFrameDelay(delay)); } else { ViewRootImpl.getRunQueue().postDelayed(what, delay); } @@ -12243,7 +12246,8 @@ 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.mViewRootImpl.mChoreographer.removeAnimationCallbacks(what, who); + mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks( + Choreographer.CALLBACK_ANIMATION, what, who); } else { ViewRootImpl.getRunQueue().removeCallbacks(what); } @@ -12261,7 +12265,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal */ public void unscheduleDrawable(Drawable who) { if (mAttachInfo != null && who != null) { - mAttachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(null, who); + mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks( + Choreographer.CALLBACK_ANIMATION, null, who); } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 14b8084..86f4226 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -877,50 +877,6 @@ public final class ViewRootImpl implements ViewParent, public void bringChildToFront(View child) { } - public void scheduleTraversals() { - if (!mTraversalScheduled) { - mTraversalScheduled = true; - mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); - scheduleFrame(); - } - } - - public void unscheduleTraversals() { - if (mTraversalScheduled) { - mTraversalScheduled = false; - mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); - } - } - - void scheduleFrame() { - if (!mFrameScheduled) { - mFrameScheduled = true; - mChoreographer.postDrawCallback(mFrameRunnable, null); - } - } - - void unscheduleFrame() { - unscheduleTraversals(); - - if (mFrameScheduled) { - mFrameScheduled = false; - mChoreographer.removeDrawCallbacks(mFrameRunnable, null); - } - } - - void doFrame() { - if (mInputEventReceiver != null) { - mInputEventReceiver.consumeBatchedInputEvents(); - } - doProcessInputEvents(); - - if (mTraversalScheduled) { - mTraversalScheduled = false; - mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); - doTraversal(); - } - } - int getHostVisibility() { return mAppVisible ? mView.getVisibility() : View.GONE; } @@ -954,41 +910,67 @@ public final class ViewRootImpl implements ViewParent, } } - private void doTraversal() { - if (mProfile) { - Debug.startMethodTracing("ViewAncestor"); + void scheduleTraversals() { + if (!mTraversalScheduled) { + mTraversalScheduled = true; + mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); + mChoreographer.postCallback( + Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); } + } - final long traversalStartTime; - if (ViewDebug.DEBUG_LATENCY) { - traversalStartTime = System.nanoTime(); - if (mLastTraversalFinishedTimeNanos != 0) { - Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals(); it has been " - + ((traversalStartTime - mLastTraversalFinishedTimeNanos) * 0.000001f) - + "ms since the last traversals finished."); - } else { - Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals()."); - } + void unscheduleTraversals() { + if (mTraversalScheduled) { + mTraversalScheduled = false; + mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); + mChoreographer.removeCallbacks( + Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); } + } - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals"); - try { - performTraversals(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - } + void doTraversal() { + if (mTraversalScheduled) { + mTraversalScheduled = false; + mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); - if (ViewDebug.DEBUG_LATENCY) { - long now = System.nanoTime(); - Log.d(ViewDebug.DEBUG_LATENCY_TAG, "performTraversals() took " - + ((now - traversalStartTime) * 0.000001f) - + "ms."); - mLastTraversalFinishedTimeNanos = now; - } + doConsumeBatchedInput(false); + doProcessInputEvents(); + + if (mProfile) { + Debug.startMethodTracing("ViewAncestor"); + } + + final long traversalStartTime; + if (ViewDebug.DEBUG_LATENCY) { + traversalStartTime = System.nanoTime(); + if (mLastTraversalFinishedTimeNanos != 0) { + Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals(); it has been " + + ((traversalStartTime - mLastTraversalFinishedTimeNanos) * 0.000001f) + + "ms since the last traversals finished."); + } else { + Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals()."); + } + } - if (mProfile) { - Debug.stopMethodTracing(); - mProfile = false; + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals"); + try { + performTraversals(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + + if (ViewDebug.DEBUG_LATENCY) { + long now = System.nanoTime(); + Log.d(ViewDebug.DEBUG_LATENCY_TAG, "performTraversals() took " + + ((now - traversalStartTime) * 0.000001f) + + "ms."); + mLastTraversalFinishedTimeNanos = now; + } + + if (mProfile) { + Debug.stopMethodTracing(); + mProfile = false; + } } } @@ -1937,6 +1919,7 @@ public final class ViewRootImpl implements ViewParent, final boolean fullRedrawNeeded = mFullRedrawNeeded; mFullRedrawNeeded = false; + mChoreographer.notifyDrawOccurred(); Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw"); try { @@ -2460,7 +2443,7 @@ public final class ViewRootImpl implements ViewParent, mInputChannel = null; } - unscheduleFrame(); + unscheduleTraversals(); } void updateConfiguration(Configuration config, boolean force) { @@ -3837,6 +3820,7 @@ public final class ViewRootImpl implements ViewParent, Message msg = mHandler.obtainMessage(MSG_IME_FINISHED_EVENT); msg.arg1 = seq; msg.arg2 = handled ? 1 : 0; + msg.setAsynchronous(true); mHandler.sendMessage(msg); } @@ -3962,11 +3946,13 @@ public final class ViewRootImpl implements ViewParent, private void scheduleProcessInputEvents() { if (!mProcessInputEventsScheduled) { mProcessInputEventsScheduled = true; - mHandler.sendEmptyMessage(MSG_PROCESS_INPUT_EVENTS); + Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); } } - private void doProcessInputEvents() { + void doProcessInputEvents() { while (mCurrentInputEvent == null && mFirstPendingInputEvent != null) { QueuedInputEvent q = mFirstPendingInputEvent; mFirstPendingInputEvent = q.mNext; @@ -4038,15 +4024,42 @@ public final class ViewRootImpl implements ViewParent, } } - final class FrameRunnable implements Runnable { + void scheduleConsumeBatchedInput() { + if (!mConsumeBatchedInputScheduled) { + mConsumeBatchedInputScheduled = true; + mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, + mConsumedBatchedInputRunnable, null); + } + } + + void unscheduleConsumeBatchedInput() { + if (mConsumeBatchedInputScheduled) { + mConsumeBatchedInputScheduled = false; + mChoreographer.removeCallbacks(Choreographer.CALLBACK_INPUT, + mConsumedBatchedInputRunnable, null); + } + } + + void doConsumeBatchedInput(boolean callback) { + if (mConsumeBatchedInputScheduled) { + mConsumeBatchedInputScheduled = false; + if (!callback) { + mChoreographer.removeCallbacks(Choreographer.CALLBACK_INPUT, + mConsumedBatchedInputRunnable, null); + } + if (mInputEventReceiver != null) { + mInputEventReceiver.consumeBatchedInputEvents(); + } + } + } + + final class TraversalRunnable implements Runnable { @Override public void run() { - mFrameScheduled = false; - doFrame(); + doTraversal(); } } - final FrameRunnable mFrameRunnable = new FrameRunnable(); - boolean mFrameScheduled; + final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); final class WindowInputEventReceiver extends InputEventReceiver { public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { @@ -4060,11 +4073,28 @@ public final class ViewRootImpl implements ViewParent, @Override public void onBatchedInputEventPending() { - scheduleFrame(); + scheduleConsumeBatchedInput(); + } + + @Override + public void dispose() { + unscheduleConsumeBatchedInput(); + super.dispose(); } } WindowInputEventReceiver mInputEventReceiver; + final class ConsumeBatchedInputRunnable implements Runnable { + @Override + public void run() { + doConsumeBatchedInput(true); + doProcessInputEvents(); + } + } + final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable = + new ConsumeBatchedInputRunnable(); + boolean mConsumeBatchedInputScheduled; + final class InvalidateOnAnimationRunnable implements Runnable { private boolean mPosted; private ArrayList<View> mViews = new ArrayList<View>(); @@ -4100,7 +4130,7 @@ public final class ViewRootImpl implements ViewParent, } if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) { - mChoreographer.removeAnimationCallbacks(this, null); + mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, this, null); mPosted = false; } } @@ -4141,7 +4171,7 @@ public final class ViewRootImpl implements ViewParent, private void postIfNeededLocked() { if (!mPosted) { - mChoreographer.postAnimationCallback(this, null); + mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); mPosted = true; } } |