繼續在網上搜索安卓按鍵模擬(其實那時都不知道用什么關鍵字好了,能想到的關鍵字都用遍了,但是搜索出來的結果,都是之前提到的那幾個依賴源碼環境和系統權限的方案)。發現有很多介紹ADB調試,向手機發送按鍵事件的文章。剛好之前做過在Root權限下,用Java調用安卓底層的Linux Shell,然后執行pm指令進行APK的安裝卸載。這時我突發奇想,能否用Shell調用ADB指令呢?
於是就進行了嘗試,使用Java執行Runtime.getRuntime().exec(“su”).getOutputStream(),獲取了一個具有Root權限的Process的輸出流對象,向其中寫入字符串即可以Root權限被Shell執行,ADB模擬按鍵的指令為 “input keyevent keyCode”,keyCode為按鍵的鍵值,例如KeyEvent.KEYCODE_VOLUME_UP表示音量加。
編譯完程序安裝執行,終於實現了預期的效果,當時非常高興。至於觸屏或鼠標事件,只要調用相應的ADB指令即可。但是有一點問題,就是反應速度非常慢,尤其是連續模擬多個按鍵的時候,甚至會死機。而按鍵精靈運行的就相當流暢,我又開始好奇按鍵精靈是怎么實現的。
后來終於還是找到了原因,模擬按鍵時,不應每次都調用Runtime.getRuntime().exec(“su”),因為每次調用這個代碼的時候,都會獲取Runtime實例,並且執行”su”請求Root權限,反應就會很慢(我的理解是相當於每次都新開一個命令行窗口);而應該只是在一開始執行一次,並獲取一個OutputStream實例,后來每次執行一條Shell指令,只需向其中寫入相應字符串,這樣就快了很多。
下面貼出可用的代碼。要求設備已經Root,不需要其他任何特殊權限或簽名。由於用的是ADB指令,兼容性也不會有太大問題。首次運行程序時(其實也就是執行Runtime.exec(“su”)的時候),會請求Root權限。
/** * 用root權限執行Linux下的Shell指令 * * @author jzj * @since 2014-09-09 */ public class RootShellCmd { private OutputStream os; /** * 執行shell指令 * * @param cmd * 指令 */ public final void exec(String cmd) { try { if (os == null) { os = Runtime.getRuntime().exec("su").getOutputStream(); } os.write(cmd.getBytes()); os.flush(); } catch (Exception e) { e.printStackTrace(); } } /** * 后台模擬全局按鍵 * * @param keyCode * 鍵值 */ public final void simulateKey(int keyCode) { exec("input keyevent " + keyCode + "\n"); } }
寫這篇文章的主要目的,並不是要強調這件事的難度,也不只是為了提出問題的解決方案(那樣就沒必要寫前面那么多過程了)。而是想把我解決問題的過程完整的寫出來,對我而言算是一個記錄,對讀者而言,沒准能從中找到一些東西。
解決這個問題之后,后來意外的發現,這個問題其實有人已經解決了,並且發了博客。不幸的是,那篇博客被大量使用前兩種思路的博客掩埋了,當時我怎么也沒找到。這篇博客地址在此:
順便說明一點,這篇博客中作者提到的缺點:反應速度較慢。前面提到我也越到了同樣的問題,也已經給出了解決方案。
android模擬按鍵問題總結[使用IWindowManager.injectKeyEvent方法]
Android中使用隱藏API(大量圖解)
通過Runtime實現,代碼如下:
- try
- {
- String keyCommand = "input keyevent " + KeyEvent.KEYCODE_MENU;
- Runtime runtime = Runtime.getRuntime();
- Process proc = runtime.exec(keyCommand);
- }
- catch (IOException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
這個代碼是模擬菜單鍵,模擬其它按鍵只需將KeyEvent.KEYCODE_MENU替換成其它鍵值。
缺點:反應速度較慢
以下附帶各KeyCode值:
- KEYCODE_UNKNOWN=0;
- KEYCODE_SOFT_LEFT=1;
- KEYCODE_SOFT_RIGHT=2;
- KEYCODE_HOME=3;
- KEYCODE_BACK=4;
- KEYCODE_CALL=5;
- KEYCODE_ENDCALL=6;
- KEYCODE_0=7;
- KEYCODE_1=8;
- KEYCODE_2=9;
- KEYCODE_3=10;
- KEYCODE_4=11;
- KEYCODE_5=12;
- KEYCODE_6=13;
- KEYCODE_7=14;
- KEYCODE_8=15;
- KEYCODE_9=16;
- KEYCODE_STAR=17;
- KEYCODE_POUND=18;
- KEYCODE_DPAD_UP=19;
- KEYCODE_DPAD_DOWN=20;
- KEYCODE_DPAD_LEFT=21;
- KEYCODE_DPAD_RIGHT=22;
- KEYCODE_DPAD_CENTER=23;
- KEYCODE_VOLUME_UP=24;
- KEYCODE_VOLUME_DOWN=25;
- KEYCODE_POWER=26;
- KEYCODE_CAMERA=27;
- KEYCODE_CLEAR=28;
- KEYCODE_A=29;
- KEYCODE_B=30;
- KEYCODE_C=31;
- KEYCODE_D=32;
- KEYCODE_E=33;
- KEYCODE_F=34;
- KEYCODE_G=35;
- KEYCODE_H=36;
- KEYCODE_I=37;
- KEYCODE_J=38;
- KEYCODE_K=39;
- KEYCODE_L=40;
- KEYCODE_M=41;
- KEYCODE_N=42;
- KEYCODE_O=43;
- KEYCODE_P=44;
- KEYCODE_Q=45;
- KEYCODE_R=46;
- KEYCODE_S=47;
- KEYCODE_T=48;
- KEYCODE_U=49;
- KEYCODE_V=50;
- KEYCODE_W=51;
- KEYCODE_X=52;
- KEYCODE_Y=53;
- KEYCODE_Z=54;
- KEYCODE_COMMA=55;
- KEYCODE_PERIOD=56;
- KEYCODE_ALT_LEFT=57;
- KEYCODE_ALT_RIGHT=58;
- KEYCODE_SHIFT_LEFT=59;
- KEYCODE_SHIFT_RIGHT=60;
- KEYCODE_TAB=61;
- KEYCODE_SPACE=62;
- KEYCODE_SYM=63;
- KEYCODE_EXPLORER=64;
- KEYCODE_ENVELOPE=65;
- KEYCODE_ENTER=66;
- KEYCODE_DEL=67;
- KEYCODE_GRAVE=68;
- KEYCODE_MINUS=69;
- KEYCODE_EQUALS=70;
- KEYCODE_LEFT_BRACKET=71;
- KEYCODE_RIGHT_BRACKET=72;
- KEYCODE_BACKSLASH=73;
- KEYCODE_SEMICOLON=74;
- KEYCODE_APOSTROPHE=75;
- KEYCODE_SLASH=76;
- KEYCODE_AT=77;
- KEYCODE_NUM=78;
- KEYCODE_HEADSETHOOK=79;
- KEYCODE_FOCUS=80;//*Camera*focus
- KEYCODE_PLUS=81;
- KEYCODE_MENU=82;
- KEYCODE_NOTIFICATION=83;
- KEYCODE_SEARCH=84;
- KEYCODE_MEDIA_PLAY_PAUSE=85;
- KEYCODE_MEDIA_STOP=86;
- KEYCODE_MEDIA_NEXT=87;
- KEYCODE_MEDIA_PREVIOUS=88;
- KEYCODE_MEDIA_REWIND=89;
- KEYCODE_MEDIA_FAST_FORWARD=90;
- KEYCODE_MUTE=91;