Use asynchronous messages for input method events.
Improves the throughput of IME event handling by ensuring that input events do not get serialized behind UI traversal and drawing messages such as when the UI is animating. Added support for creating an asynchronous Handler as part of a HandlerCaller. It turns out we should be using an asynchronous Handler not only in IME dispatch but also in accessibility and wallpaper events where HandlerCaller is used. So fixed those services to also use an asynchronous Handler. Change-Id: I0b19140c9d5ca6ee300c1a150c48312fd55ed8eb
This commit is contained in:
@@ -556,7 +556,7 @@ public abstract class AccessibilityService extends Service {
|
||||
public IAccessibilityServiceClientWrapper(Context context, Looper looper,
|
||||
Callbacks callback) {
|
||||
mCallback = callback;
|
||||
mCaller = new HandlerCaller(context, looper, this);
|
||||
mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
|
||||
}
|
||||
|
||||
public void setConnection(IAccessibilityServiceConnection connection, int connectionId) {
|
||||
|
||||
@@ -70,7 +70,8 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
|
||||
|
||||
public IInputMethodSessionWrapper(Context context,
|
||||
InputMethodSession inputMethodSession) {
|
||||
mCaller = new HandlerCaller(context, this);
|
||||
mCaller = new HandlerCaller(context, null,
|
||||
this, true /*asyncHandler*/);
|
||||
mInputMethodSession = inputMethodSession;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,8 @@ class IInputMethodWrapper extends IInputMethod.Stub
|
||||
public IInputMethodWrapper(AbstractInputMethodService context,
|
||||
InputMethod inputMethod) {
|
||||
mTarget = new WeakReference<AbstractInputMethodService>(context);
|
||||
mCaller = new HandlerCaller(context.getApplicationContext(), this);
|
||||
mCaller = new HandlerCaller(context.getApplicationContext(), null,
|
||||
this, true /*asyncHandler*/);
|
||||
mInputMethod = new WeakReference<InputMethod>(inputMethod);
|
||||
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
|
||||
}
|
||||
|
||||
@@ -967,7 +967,7 @@ public abstract class WallpaperService extends Service {
|
||||
mCaller = new HandlerCaller(context,
|
||||
mCallbackLooper != null
|
||||
? mCallbackLooper : context.getMainLooper(),
|
||||
this);
|
||||
this, true /*asyncHandler*/);
|
||||
mConnection = conn;
|
||||
mWindowToken = windowToken;
|
||||
mWindowType = windowType;
|
||||
|
||||
@@ -40,7 +40,7 @@ class SimulatedTrackball {
|
||||
private static final int MAX_TAP_TIME = 250;
|
||||
// Where the cutoff is for determining an edge swipe
|
||||
private static final float EDGE_SWIPE_THRESHOLD = 0.9f;
|
||||
private static final int FLICK_MSG_ID = 313;
|
||||
private static final int MSG_FLICK = 313;
|
||||
// TODO: Pass touch slop from the input device
|
||||
private static final int TOUCH_SLOP = 30;
|
||||
|
||||
@@ -75,8 +75,11 @@ class SimulatedTrackball {
|
||||
// Has the TouchSlop constraint been invalidated
|
||||
private boolean mAlwaysInTapRegion = true;
|
||||
|
||||
// Most recent event. Used to determine what device sent the event.
|
||||
private MotionEvent mRecentEvent;
|
||||
// Information from the most recent event.
|
||||
// Used to determine what device sent the event during a fling.
|
||||
private int mLastSource;
|
||||
private int mLastMetaState;
|
||||
private int mLastDeviceId;
|
||||
|
||||
// TODO: Currently using screen dimensions tuned to a Galaxy Nexus, need to
|
||||
// read this from a config file instead
|
||||
@@ -101,33 +104,34 @@ class SimulatedTrackball {
|
||||
mTouchSlopSquared = mTouchSlop * mTouchSlop;
|
||||
}
|
||||
|
||||
private final Handler mHandler = new Handler(new Callback() {
|
||||
private final Handler mHandler = new Handler(true /*async*/) {
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
if (msg.what != FLICK_MSG_ID)
|
||||
return false;
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_FLICK: {
|
||||
final long time = SystemClock.uptimeMillis();
|
||||
ViewRootImpl viewroot = (ViewRootImpl) msg.obj;
|
||||
// Send the key
|
||||
viewroot.enqueueInputEvent(new KeyEvent(time, time,
|
||||
KeyEvent.ACTION_DOWN, msg.arg2, 0, mLastMetaState,
|
||||
mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
|
||||
viewroot.enqueueInputEvent(new KeyEvent(time, time,
|
||||
KeyEvent.ACTION_UP, msg.arg2, 0, mLastMetaState,
|
||||
mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
|
||||
|
||||
final long time = SystemClock.uptimeMillis();
|
||||
ViewRootImpl viewroot = (ViewRootImpl) msg.obj;
|
||||
// Send the key
|
||||
viewroot.enqueueInputEvent(new KeyEvent(time, time,
|
||||
KeyEvent.ACTION_DOWN, msg.arg2, 0, mRecentEvent.getMetaState(),
|
||||
mRecentEvent.getDeviceId(), 0,
|
||||
KeyEvent.FLAG_FALLBACK, mRecentEvent.getSource()));
|
||||
viewroot.enqueueInputEvent(new KeyEvent(time, time,
|
||||
KeyEvent.ACTION_UP, msg.arg2, 0, mRecentEvent.getMetaState(),
|
||||
mRecentEvent.getDeviceId(), 0,
|
||||
KeyEvent.FLAG_FALLBACK, mRecentEvent.getSource()));
|
||||
Message msgCopy = Message.obtain(msg);
|
||||
// Increase the delay by the decay factor
|
||||
msgCopy.arg1 = (int) Math.ceil(mFlickDecay * msgCopy.arg1);
|
||||
if (msgCopy.arg1 <= mMaxRepeatDelay) {
|
||||
// Send the key again in arg1 milliseconds
|
||||
mHandler.sendMessageDelayed(msgCopy, msgCopy.arg1);
|
||||
// Increase the delay by the decay factor and resend
|
||||
final int delay = (int) Math.ceil(mFlickDecay * msg.arg1);
|
||||
if (delay <= mMaxRepeatDelay) {
|
||||
Message msgCopy = Message.obtain(msg);
|
||||
msgCopy.arg1 = delay;
|
||||
msgCopy.setAsynchronous(true);
|
||||
mHandler.sendMessageDelayed(msgCopy, delay);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
public void updateTrackballDirection(ViewRootImpl viewroot, MotionEvent event) {
|
||||
// Store what time the touchpad event occurred
|
||||
@@ -148,7 +152,7 @@ class SimulatedTrackball {
|
||||
mEdgeSwipePossible = true;
|
||||
}
|
||||
// Clear any flings
|
||||
mHandler.removeMessages(FLICK_MSG_ID);
|
||||
mHandler.removeMessages(MSG_FLICK);
|
||||
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
@@ -245,15 +249,20 @@ class SimulatedTrackball {
|
||||
if (mMinFlickDistanceSquared <= xMoveSquared + yMoveSquared &&
|
||||
time - mLastTouchPadEventTimeMs <= MAX_TAP_TIME &&
|
||||
mKeySendRateMs <= mMaxRepeatDelay && mKeySendRateMs > 0) {
|
||||
Message message = Message.obtain(mHandler, FLICK_MSG_ID,
|
||||
mLastDeviceId = event.getDeviceId();
|
||||
mLastSource = event.getSource();
|
||||
mLastMetaState = event.getMetaState();
|
||||
|
||||
Message message = Message.obtain(mHandler, MSG_FLICK,
|
||||
mKeySendRateMs, mLastKeySent, viewroot);
|
||||
mRecentEvent = event;
|
||||
message.setAsynchronous(true);
|
||||
mHandler.sendMessageDelayed(message, mKeySendRateMs);
|
||||
}
|
||||
}
|
||||
mEdgeSwipePossible = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Store touch event position and time
|
||||
mLastTouchPadEventTimeMs = time;
|
||||
mLastTouchpadXPosition = event.getX();
|
||||
|
||||
@@ -24,38 +24,32 @@ import android.os.Message;
|
||||
public class HandlerCaller {
|
||||
|
||||
public final Context mContext;
|
||||
|
||||
|
||||
final Looper mMainLooper;
|
||||
final Handler mH;
|
||||
|
||||
final Callback mCallback;
|
||||
|
||||
class MyHandler extends Handler {
|
||||
MyHandler(Looper looper) {
|
||||
super(looper);
|
||||
MyHandler(Looper looper, boolean async) {
|
||||
super(looper, null, async);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
mCallback.executeMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface Callback {
|
||||
public void executeMessage(Message msg);
|
||||
}
|
||||
|
||||
public HandlerCaller(Context context, Callback callback) {
|
||||
mContext = context;
|
||||
mMainLooper = context.getMainLooper();
|
||||
mH = new MyHandler(mMainLooper);
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
public HandlerCaller(Context context, Looper looper, Callback callback) {
|
||||
public HandlerCaller(Context context, Looper looper, Callback callback,
|
||||
boolean asyncHandler) {
|
||||
mContext = context;
|
||||
mMainLooper = looper;
|
||||
mH = new MyHandler(mMainLooper);
|
||||
mMainLooper = looper != null ? looper : context.getMainLooper();
|
||||
mH = new MyHandler(mMainLooper, asyncHandler);
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
|
||||
@@ -602,12 +602,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
mHandler = new Handler(this);
|
||||
mIWindowManager = IWindowManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.WINDOW_SERVICE));
|
||||
mCaller = new HandlerCaller(context, new HandlerCaller.Callback() {
|
||||
mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
|
||||
@Override
|
||||
public void executeMessage(Message msg) {
|
||||
handleMessage(msg);
|
||||
}
|
||||
});
|
||||
}, true /*asyncHandler*/);
|
||||
mWindowManagerService = windowManager;
|
||||
mHardKeyboardListener = new HardKeyboardListener();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user