我通常会使用 adb shell input keyevent 4
来模拟发送 keyevent 事件, 那么如何以代码的方式发送keyevent呢?这就翻一翻 input 的源码了,Input.java 源码处理如下:
Input() {
COMMANDS.put("text", new InputText());
COMMANDS.put("keyevent", new InputKeyEvent()); //1. 处理keyevent类型
COMMANDS.put("tap", new InputTap());
COMMANDS.put("swipe", new InputSwipe());
COMMANDS.put("draganddrop", new InputDragAndDrop());
COMMANDS.put("press", new InputPress());
COMMANDS.put("roll", new InputRoll());
COMMANDS.put("motionevent", new InputMotionEvent());
}
处理keyevent事件类型的是InputKeyEvent类型:
class InputKeyEvent implements InputCmd {
@Override
public void run(int inputSource, int displayId) {
String arg = nextArgRequired();
final boolean longpress = "--longpress".equals(arg);
if (longpress) {
arg = nextArgRequired();
}
do {
final int keycode = KeyEvent.keyCodeFromString(arg);
sendKeyEvent(inputSource, keycode, longpress, displayId);
} while ((arg = nextArg()) != null);
}
private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) {
final long now = SystemClock.uptimeMillis();
int repeatCount = 0;
//1. 创建KeyEvent
KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount,
0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
inputSource);
//2. 设置要发送的屏幕id
event.setDisplayId(displayId);
//3. 发送该keyevent的ACTION_DOWN事件
injectKeyEvent(event);
if (longpress) {
repeatCount++;
injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount,
KeyEvent.FLAG_LONG_PRESS));
}
//4. 发送该keyevent的ACTION_UP事件
injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
}
}
再看看 injectKeyEvent
方法是如何发送的:
private static void injectKeyEvent(KeyEvent event) {
InputManager.getInstance().injectInputEvent(event,
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
}
原来是调用InputManager的injectInputEvent方法即可:
@UnsupportedAppUsage
public boolean injectInputEvent(InputEvent event, int mode) {
if (event == null) {
throw new IllegalArgumentException("event must not be null");
}
if (mode != INJECT_INPUT_EVENT_MODE_ASYNC
&& mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
&& mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
throw new IllegalArgumentException("mode is invalid");
}
try {
return mIm.injectInputEvent(event, mode);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
但是这个方法添加@UnsupportedAppUsage
注解,App 要使用一些骚操作(不是本文重点)。照葫芦画瓢的使用:
private void sendDownAndUpKeyEvents(int keyCode) {
final long downTime = SystemClock.uptimeMillis();
sendKeyEventIdentityCleared(keyCode, KeyEvent.ACTION_DOWN, downTime, downTime);
sendKeyEventIdentityCleared(
keyCode, KeyEvent.ACTION_UP, downTime, SystemClock.uptimeMillis());
}
private void sendKeyEventIdentityCleared(int keyCode, int action, long downTime, long time) {
KeyEvent event = KeyEvent.obtain(downTime, time, action, keyCode, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
InputDevice.SOURCE_KEYBOARD, null);
InputManager.getInstance()
.injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
event.recycle();
}
以返回事件为例:
sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
搞定。