From 1f4786bbe1ed7ae54b94cd52c0e1a4a826fecd68 Mon Sep 17 00:00:00 2001 From: Chet Haase Date: Wed, 2 Nov 2011 10:51:52 -0700 Subject: Improve Launcher drag performance. Launcher swiping looks choppy. It's because we deliver the motion events of the drags asynchronously, and sometimes we may get an event to redraw before the motion event is posted, so we end up drawing again without updating to the latest motion information. This fix makes input event processing more proactive. Every time we run ViewRootImpl.performTraversals() (which is what happens whenever we need to layout, measure, and/or draw), we first process all pending input events, ensuring that we are completely up-to-date with posted events prior to drawing, so that the drawing we do is synchronous with the events we've received. This eliminates the choppiness and means that we can now get the full refresh rate on the screen with drag events. The fix was done for Launcher, but it is pervasive in the system, so this may fix other laggy drag behavior as well. Change-Id: I8dbed6acadc2662f317f736e769f536f555701aa --- core/java/android/view/ViewRootImpl.java | 105 +++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 6 deletions(-) (limited to 'core/java/android') diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 24423c327ecc..f7078ecd3ec6 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -218,6 +218,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, boolean mNewSurfaceNeeded; boolean mHasHadWindowFocus; boolean mLastWasImTarget; + InputEventMessage mPendingInputEvents = null; boolean mWindowAttributesChanged = false; int mWindowAttributesChangesFlag = 0; @@ -832,10 +833,24 @@ public final class ViewRootImpl extends Handler implements ViewParent, } } + private void processInputEvents(boolean outOfOrder) { + while (mPendingInputEvents != null) { + handleMessage(mPendingInputEvents.mMessage); + InputEventMessage tmpMessage = mPendingInputEvents; + mPendingInputEvents = mPendingInputEvents.mNext; + tmpMessage.recycle(); + if (outOfOrder) { + removeMessages(PROCESS_INPUT_EVENTS); + } + } + } + private void performTraversals() { // cache mView since it is used so much below... final View host = mView; + processInputEvents(true); + if (DBG) { System.out.println("======================================"); System.out.println("performTraversals"); @@ -2336,6 +2351,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1021; public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022; public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1023; + public final static int PROCESS_INPUT_EVENTS = 1024; @Override public String getMessageName(Message message) { @@ -2388,7 +2404,9 @@ public final class ViewRootImpl extends Handler implements ViewParent, return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID"; case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT"; - + case PROCESS_INPUT_EVENTS: + return "PROCESS_INPUT_EVENTS"; + } return super.getMessageName(message); } @@ -2447,6 +2465,9 @@ public final class ViewRootImpl extends Handler implements ViewParent, case DISPATCH_GENERIC_MOTION: deliverGenericMotionEvent((MotionEvent) msg.obj, msg.arg1 != 0); break; + case PROCESS_INPUT_EVENTS: + processInputEvents(false); + break; case DISPATCH_APP_VISIBILITY: handleAppVisibility(msg.arg1 != 0); break; @@ -3744,7 +3765,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, msg.obj = ri; sendMessage(msg); } - + private long mInputEventReceiveTimeNanos; private long mInputEventDeliverTimeNanos; private long mInputEventDeliverPostImeTimeNanos; @@ -3762,6 +3783,78 @@ public final class ViewRootImpl extends Handler implements ViewParent, } }; + /** + * Utility class used to queue up input events which are then handled during + * performTraversals(). Doing it this way allows us to ensure that we are up to date with + * all input events just prior to drawing, instead of placing those events on the regular + * handler queue, potentially behind a drawing event. + */ + static class InputEventMessage { + Message mMessage; + InputEventMessage mNext; + + private static final Object sPoolSync = new Object(); + private static InputEventMessage sPool; + private static int sPoolSize = 0; + + private static final int MAX_POOL_SIZE = 10; + + private InputEventMessage(Message m) { + mMessage = m; + mNext = null; + } + + /** + * Return a new Message instance from the global pool. Allows us to + * avoid allocating new objects in many cases. + */ + public static InputEventMessage obtain(Message msg) { + synchronized (sPoolSync) { + if (sPool != null) { + InputEventMessage m = sPool; + sPool = m.mNext; + m.mNext = null; + sPoolSize--; + m.mMessage = msg; + return m; + } + } + return new InputEventMessage(msg); + } + + /** + * Return the message to the pool. + */ + public void recycle() { + mMessage.recycle(); + synchronized (sPoolSync) { + if (sPoolSize < MAX_POOL_SIZE) { + mNext = sPool; + sPool = this; + sPoolSize++; + } + } + + } + } + + /** + * Place the input event message at the end of the current pending list + */ + private void enqueueInputEvent(Message msg, long when) { + InputEventMessage inputMessage = InputEventMessage.obtain(msg); + if (mPendingInputEvents == null) { + mPendingInputEvents = inputMessage; + } else { + InputEventMessage currMessage = mPendingInputEvents; + while (currMessage.mNext != null) { + currMessage = currMessage.mNext; + } + currMessage.mNext = inputMessage; + } + sendEmptyMessageAtTime(PROCESS_INPUT_EVENTS, when); + } + public void dispatchKey(KeyEvent event) { dispatchKey(event, false); } @@ -3786,7 +3879,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (LOCAL_LOGV) Log.v( TAG, "sending key " + event + " to " + mView); - sendMessageAtTime(msg, event.getEventTime()); + enqueueInputEvent(msg, event.getEventTime()); } private void dispatchMotion(MotionEvent event, boolean sendDone) { @@ -3804,21 +3897,21 @@ public final class ViewRootImpl extends Handler implements ViewParent, Message msg = obtainMessage(DISPATCH_POINTER); msg.obj = event; msg.arg1 = sendDone ? 1 : 0; - sendMessageAtTime(msg, event.getEventTime()); + enqueueInputEvent(msg, event.getEventTime()); } private void dispatchTrackball(MotionEvent event, boolean sendDone) { Message msg = obtainMessage(DISPATCH_TRACKBALL); msg.obj = event; msg.arg1 = sendDone ? 1 : 0; - sendMessageAtTime(msg, event.getEventTime()); + enqueueInputEvent(msg, event.getEventTime()); } private void dispatchGenericMotion(MotionEvent event, boolean sendDone) { Message msg = obtainMessage(DISPATCH_GENERIC_MOTION); msg.obj = event; msg.arg1 = sendDone ? 1 : 0; - sendMessageAtTime(msg, event.getEventTime()); + enqueueInputEvent(msg, event.getEventTime()); } public void dispatchAppVisibility(boolean visible) { -- cgit v1.2.3