diff options
| author | Jeff Brown <jeffbrown@google.com> | 2012-02-07 14:46:57 -0800 |
|---|---|---|
| committer | Jeff Brown <jeffbrown@google.com> | 2012-02-13 10:28:41 -0800 |
| commit | 072ec96a4900d4616574733646ee46311cb5d2cb (patch) | |
| tree | 9172f3e3295f9d4ff3517a47dec360c2f0a99948 /core/java/android/view/InputEventReceiver.java | |
| parent | 1adee11b5e644c74a2ed40344f4836de3bd3ac56 (diff) | |
Implement batching of input events on the consumer side.
To support this feature, the input dispatcher now allows input
events to be acknowledged out-of-order. As a result, the
consumer can choose to defer handling an input event from one
device (because it is building a big batch) while continuing
to handle input events from other devices.
The InputEventReceiver now sends a notification when a batch
is pending. The ViewRoot handles this notification by scheduling
a draw on the next sync. When the draw happens, the InputEventReceiver
is instructed to consume all pending batched input events, the
input event queue is fully processed (as much as possible),
and then the ViewRoot performs traversals as usual.
With these changes in place, the input dispatch latency is
consistently less than one frame as long as the application itself
isn't stalled. Input events are delivered to the application
as soon as possible and are handled as soon as possible. In practice,
it is no longer possible for an application to build up a huge
backlog of touch events.
This is part of a series of changes to improve input system pipelining.
Bug: 5963420
Change-Id: I42c01117eca78f12d66d49a736c1c122346ccd1d
Diffstat (limited to 'core/java/android/view/InputEventReceiver.java')
| -rw-r--r-- | core/java/android/view/InputEventReceiver.java | 57 |
1 files changed, 48 insertions, 9 deletions
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index 764d8dcaba2a..6a457ece9b9d 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -21,6 +21,7 @@ import dalvik.system.CloseGuard; import android.os.Looper; import android.os.MessageQueue; import android.util.Log; +import android.util.SparseIntArray; /** * Provides a low-level mechanism for an application to receive input events. @@ -38,13 +39,14 @@ public abstract class InputEventReceiver { private InputChannel mInputChannel; private MessageQueue mMessageQueue; - // The sequence number of the event that is in progress. - private int mEventSequenceNumberInProgress = -1; + // Map from InputEvent sequence numbers to dispatcher sequence numbers. + private final SparseIntArray mSeqMap = new SparseIntArray(); private static native int nativeInit(InputEventReceiver receiver, InputChannel inputChannel, MessageQueue messageQueue); private static native void nativeDispose(int receiverPtr); - private static native void nativeFinishInputEvent(int receiverPtr, boolean handled); + private static native void nativeFinishInputEvent(int receiverPtr, int seq, boolean handled); + private static native void nativeConsumeBatchedInputEvents(int receiverPtr); /** * Creates an input event receiver bound to the specified input channel. @@ -104,12 +106,25 @@ public abstract class InputEventReceiver { } /** + * Called when a batched input event is pending. + * + * The batched input event will continue to accumulate additional movement + * samples until the recipient calls {@link #consumeBatchedInputEvents} or + * an event is received that ends the batch and causes it to be consumed + * immediately (such as a pointer up event). + */ + public void onBatchedInputEventPending() { + consumeBatchedInputEvents(); + } + + /** * Finishes an input event and indicates whether it was handled. + * Must be called on the same Looper thread to which the receiver is attached. * * @param event The input event that was finished. * @param handled True if the event was handled. */ - public void finishInputEvent(InputEvent event, boolean handled) { + public final void finishInputEvent(InputEvent event, boolean handled) { if (event == null) { throw new IllegalArgumentException("event must not be null"); } @@ -117,23 +132,47 @@ public abstract class InputEventReceiver { Log.w(TAG, "Attempted to finish an input event but the input event " + "receiver has already been disposed."); } else { - if (event.getSequenceNumber() != mEventSequenceNumberInProgress) { + int index = mSeqMap.indexOfKey(event.getSequenceNumber()); + if (index < 0) { Log.w(TAG, "Attempted to finish an input event that is not in progress."); } else { - mEventSequenceNumberInProgress = -1; - nativeFinishInputEvent(mReceiverPtr, handled); + int seq = mSeqMap.valueAt(index); + mSeqMap.removeAt(index); + nativeFinishInputEvent(mReceiverPtr, seq, handled); } } event.recycleIfNeededAfterDispatch(); } + /** + * Consumes all pending batched input events. + * Must be called on the same Looper thread to which the receiver is attached. + * + * This method forces all batched input events to be delivered immediately. + * Should be called just before animating or drawing a new frame in the UI. + */ + public final void consumeBatchedInputEvents() { + if (mReceiverPtr == 0) { + Log.w(TAG, "Attempted to consume batched input events but the input event " + + "receiver has already been disposed."); + } else { + nativeConsumeBatchedInputEvents(mReceiverPtr); + } + } + // Called from native code. @SuppressWarnings("unused") - private void dispatchInputEvent(InputEvent event) { - mEventSequenceNumberInProgress = event.getSequenceNumber(); + private void dispatchInputEvent(int seq, InputEvent event) { + mSeqMap.put(event.getSequenceNumber(), seq); onInputEvent(event); } + // Called from native code. + @SuppressWarnings("unused") + private void dispatchBatchedInputEventPending() { + onBatchedInputEventPending(); + } + public static interface Factory { public InputEventReceiver createInputEventReceiver( InputChannel inputChannel, Looper looper); |
