android 跨程式點選方式總結

maweiliang發表於2019-03-04

我們在日常的測試中,經常需要模擬使用者點選等操作來實現模擬使用者各種輸入功能,在這裡歸納總結一下幾種點選方式,以及它們各自的優缺點,目前實現跨程式點選方式大致會有一下四種方式

android 跨程式點選方式總結

  • 一 adb shell 命令下輸入 input命令,我們會看它支援的事件型別如下圖:

android 跨程式點選方式總結
我們重點來看一下input tap 命令,它是模擬使用者觸屏事件,具體的方式為:
input tap x,y;x,y是要點選的座標點,我們再來看看在我們的程式程式碼中怎麼呼叫

private BufferedInputStream stdin = null;
private BufferedOutputStream stdout = null;
private BufferedInputStream erroeOut = null;
private static Process mProcess = null;

public Shell(String su) throws IOException, InterruptedException {

  mProcess = Runtime.getRuntime().exec(su);
  this.stdin = new BufferedInputStream(mProcess.getInputStream());
  this.stdout = new BufferedOutputStream(mProcess.getOutputStream());
  this.erroeOut = new BufferedInputStream(mProcess.getErrorStream());
  this.read();
}

public void write(String value) throws IOException {
  this.stdout.write((value + "\n").getBytes());
  this.stdout.flush();
}複製程式碼

上面是對執行shell命令的一個簡單分裝,下面我們只要傳入對應的命令即可,比如我們點選螢幕上(100,200)這個點

Shell shell = new Shell("su");
shell.write( "input tap 100 200" );複製程式碼

執行這個命令需要手機要有Root許可權,adb shell input 相對來說使用比較簡單,但是它執行效率比較慢。

  • 二:monkey
    大家知道Monkey使用來做壓力測試的,其實當我們執行monkey的時候會啟動“com.android.commands.monkey”這樣一個程式,monkey的各種點選滑動操作都是在此程式中實現的,所以如果我們能夠將事件傳送到該程式中,那麼我們就可以實現跨程式點選的能力了,我們來看看如何實現。
    啟動“com.android.commands.monkey”程式,可以是使用monkey -port 3131,monkey的程式啟動後,怎麼能夠通訊能,其實我們可以通過socket的方式來建立連線,通過連線127.0.0.1地址和埠3131就能與monkey程式建立通訊,下面我們就來實現它
    android 跨程式點選方式總結
    首選判斷一下monkey程式是否已經啟,已經執行的話將它kill後,再次啟用。
    android 跨程式點選方式總結
    接下來我們就可以建立一個socket,連線到3131埠,也就是們剛才啟動的monkey程式。
    android 跨程式點選方式總結
    連上Monkey程式之後,我們就可以傳送點選事件了,monkey的點選事件時有down和up兩部分組成的,格式如下:
    touch down x y
    touch up x y複製程式碼
    它不僅可以傳送點選事件,也可以傳送系統keycode等事件,monkey執行的效率上來說快了很多,但是有時後他的穩定性不夠好,而且它和UiAutomator不能同時使用,兩者相互衝突
  • 三:Instrumentation方式:
    如果我們在自己的應用可以通過下面的這種方式來傳送一個點選事件,
    long downTime = SystemClock.uptimeMillis();
    MotionEvent tapDownEvent = MotionEvent.obtain(downTime, downTime,MotionEvent.ACTION_DOWN, x, y, 0);
    MotionEvent tapUpEvent = MotionEvent.obtain(downTime+100,downTime+100, MotionEvent.ACTION_UP, x, y, 0);
    mInstrumentation.sendPointerSync(tapDownEvent);
    mInstrumentation.sendPointerSync(tapUpEvent);
    tapDownEvent.recycle();
    tapUpEvent.recycle();複製程式碼
    可是如果要程式跨程式點選就不行了,它會報這個錯誤,Permission denied,injecting event from....
    造成這個錯誤的原因是因為我們從一個應用向另一個應用傳送點選事件,而這兩個應用的UID不同導致的。
    所以要想實現跨程式的能力,可以有兩種方式,一是hook被測應用的程式,通過程式通訊的方式在被測應用中實現點選;另一種方式就是通過hook的方式修改測試應用的UID
    修改被測應用的UID,通過hook方式通過許可權檢查就是:hook中native InjectInputEvent,將uid的值改為0,這樣就能夠通過裡面hasInjectPermission的許可權驗證了。
    findAndHookMethod("com.android.server.input.InputManagerService", lpparam.classLoader,
                      "nativeInjectInputEvent", int.class, lpparam.classLoader.loadClass("android.view.InputEvent"),
                      int.class, int.class, int.class, int.class, int.class, new XC_MethodHook() {
                          @Override
                          protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                              XposedBridge.log("lzf called nativeInjectInputEvent:" + param.args[3]);
                              XposedBridge.log("uid is :" + TargetUid + " " + param.args[3]);
                              if ((Integer) param.args[3] == TargetUid) {
                                  XposedBridge.log("here:" + param.args[3]);
                                  param.args[3] = 0;
                              }
                          }
                      });複製程式碼
    TargetUid是我們要注入的應用的UID,
    更加詳細的原因可以參考www.hizher.com/pageContent…
  • 四:sendevent方式:
    Android提供了這兩個方便的工具來處理輸入事件
    getevent:可以檢視裝置的輸入資訊
    sendevent:注入輸入事件
    使用getevent獲取裝置的輸入資訊:
    android 跨程式點選方式總結
    這是N5裝置上的資訊(不同裝置的資訊可能不同),可以看到N5手機中6個event裝置, /dev/input/event1是傳送觸控的裝置,我們點選螢幕的某一點,輸出下列資訊
    /dev/input/event1: EV_ABS       ABS_MT_TRACKING_ID   00000005
    /dev/input/event1: EV_ABS       ABS_MT_POSITION_X    0000018b
    /dev/input/event1: EV_ABS       ABS_MT_POSITION_Y    00000529
    /dev/input/event1: EV_ABS       ABS_MT_PRESSURE      00000030
    /dev/input/event1: EV_SYN       SYN_REPORT           00000000
    /dev/input/event1: EV_ABS       ABS_MT_POSITION_X    0000018a
    /dev/input/event1: EV_ABS       ABS_MT_POSITION_Y    00000528
    /dev/input/event1: EV_SYN       SYN_REPORT           00000000
    /dev/input/event1: EV_ABS       ABS_MT_POSITION_X    00000189
    /dev/input/event1: EV_ABS       ABS_MT_POSITION_Y    00000527
    /dev/input/event1: EV_ABS       ABS_MT_PRESSURE      0000002e
    /dev/input/event1: EV_ABS       ABS_MT_TOUCH_MAJOR   00000003
    /dev/input/event1: EV_SYN       SYN_REPORT           00000000
    /dev/input/event1: EV_ABS       ABS_MT_TRACKING_ID   ffffffff
    /dev/input/event1: EV_SYN       SYN_REPORT           00000000複製程式碼
    看一下上面每一項的說明:
    ABS_MT_TRACKING_ID:報告觸碰跟蹤的ID,是一個非負的任意整數,用來分辨多個同時的操作。例如,當多個手指觸碰裝置,在手指還在螢幕上時每個手指繫結一個獨立的跟蹤ID,當手指離開螢幕後,跟蹤ID可能被重新使用(可選項)
    ABS_MT_POSITION_X:觸控事件的X軸座標(必選項)
    ABS_MT_POSITION_Y:觸控事件的y軸座標(必選項)
    ABS_MT_PRESSURE:觸控事件壓力大小或者訊號強度(可選項)
    SYN_REPORT:當以觸控事件up的時候傳送的訊號量
    更多的參考:多點觸控裝置使用以下Linux輸入事件
    每個sendevent命令都需要4個引數
    device_name (string)
    event_type (decimal int)
    event_code (decimal int)
    value (decimal int)
    上面的座標座標是十六進位制轉化為十進位制後,傳送點選事件
    sendevent /dev/input/event1 3 53 395
    sendevent /dev/input/event1 3 54 1321
    sendevent /dev/input/event1 0 0 0複製程式碼
    這種方式,每個裝置的事件型別不一致,需要匹配不同的機型.

相關文章