2018年11月5日 星期一

16-input三兄弟keyevent

語法 input keyevent [--longpress] <key code number or name>
說明 與 MonkeyDevice.press(<key name>, MonkeyDevice.DOWN_AND_UP) 的行為類似。
Key name,可查閱 KeyEvent 裡以 KEYCODE_ 為開頭的名稱。
Key code number,可查閱 KeyEvent 裡以 KEYCODE_ 為開頭的名稱的 constant value。
--longpress 此參數目前無法達成長壓按鍵的效果。

咱們直接看例子:

# 匯入所需模組
import os

from com.android.monkeyrunner import MonkeyDevice, MonkeyImage, MonkeyRunner

# 連接 Android 裝置
device = MonkeyRunner.waitForConnection()

# A: 連續送出 5 次 MonkeyDevice.shell()
for i in range(5):
  device.shell('input keyevent KEYCODE_VOLUME_UP')

# B: 連續送出 5 次 adb shell
for i in range(5):
  os.system('adb shell input keyevent KEYCODE_VOLUME_UP')

# C: 送出 1 次 MonkeyDevice.shell()
param = ''
for i in range(5):
  param += 'input keyevent KEYCODE_VOLUME_UP; '

device.shell(param)

# D: 送出 1 次 adb shell
param = ''
for i in range(5):
  param += 'input keyevent KEYCODE_VOLUME_UP; '

os.system('adb shell ' + param)

以上 A、B、C 和 D 四種都是合法的 shell 命令(雖然按鍵被按下後到要按下一個按鍵之間停頓的時間會不同,但稍微想一下應該就能明白為什麼會有這現象),所以大部分看官應該會認為這四種都能正常執行。也或許有些看官會想起 05-按下那個按鍵!猴子!(MonkeyDevice.press) 裡遇到的「連續送出 MonkeyDevice.press() 時會丟出 exception」的問題,所以認為會有失敗的測項,咱們直接跑看看吧:
ASUS Z00LD (Android 6.0.1) Nexus 5 (Android 8.1.0)
A Pass Pass
B Pass Pass
C Fail Pass
D Pass Pass

非常奇妙!Nexus 5 (Android 8.1.0) 在這四個項目都可正常運作,但 ASUS Z00LD (Android 6.0.1) 卻會死在 C 測項。想當然耳來看, C 與 D 是一樣的,但結果卻與手機的版本似乎有點關係?!

若看官遇到的 exception 也和本喵一樣是 com.android.ddmlib.ShellCommandUnresponsiveException 的話,那原因多半是因為執行逾時了!還記得 13-最終兵器MonkeyDevice.shell 裡所說的 MonkeyDevice.shell() 可以接受一個 timeout 的參數嗎?預設上是 5 秒內要完成指令,可是因為要求執行的命令太多了,有些裝置無法在限時內完成,所以便丟出 exception。解決方法很簡單,只要指定更長的 timeout 給 MonkeyDevice.shell() 就好了:
# C: 送出 1 次 MonkeyDevice.shell()
param = ''
for i in range(5):
  param += 'input keyevent KEYCODE_VOLUME_UP; '

# 將 timeout 設為 30 秒
device.shell(param, 30 * 1000)
如此一來,ASUS Z00LD (Android 6.0.1) 便可完成測項 C 了!只是每次都要對不同裝置和指令微調 timeout 的設定並不是一件輕鬆的事情,若非必要,本喵還是覺得直接用 Python 的 os.system()subprocess.Popen() 來呼叫 shell 比較方便。

那麼來看看為什麼 --longpress 這個參數沒作用的原因吧!咱們當然是由原始碼 Input.java 來下手喽:
private void sendKeyEvent(int inputSource, int keyCode, boolean longpress) {
    long now = SystemClock.uptimeMillis();
    injectKeyEvent(new KeyEvent(
        now,
        now,
        KeyEvent.ACTION_DOWN,
        keyCode,
        0,
        0,
        KeyCharacterMap.VIRTUAL_KEYBOARD,
        0,
        0,
        inputSource
    ));
    if (longpress) {
        injectKeyEvent(new KeyEvent(
            now,
            now,
            KeyEvent.ACTION_DOWN,
            keyCode,
            1,
            0,
            KeyCharacterMap.VIRTUAL_KEYBOARD,
            0,
            KeyEvent.FLAG_LONG_PRESS,
            inputSource
        ));
    }
    injectKeyEvent(new KeyEvent(
        now,
        now,
        KeyEvent.ACTION_UP,
        keyCode,
        0,
        0,
        KeyCharacterMap.VIRTUAL_KEYBOARD,
        0,
        0,
        inputSource
    ));
}

看官們理解原因所在了嗎?雖然第 16 行會送出代表 long press 的 KeyEvent,但 KeyEvent.ACTION_DOWN 和 KeyEvent.ACTION_UP 兩個事件之間的間隔實在太短了,根本無法觸發裝置執行對應的動作 O___Oa

沒有留言:

張貼留言