Android 跨进程点击方法小结

字数 1069阅读 1893

写一些测试工具经常用到点击。本文总结了一些跨进程点击的几种方法。
由于要跨进程注入,所以本文讲的方法都是需要root权限的

1、使用adb命令input tap.

该命令用法很简单,后面直接接x、y坐标即可

input tap 100 100 //点击坐标(100,100)

2、使用sendevent命令

sendevent命令用起来稍微复杂一点,这个命令是用来给设备发送事件的,要使用这个命令,首先需要了解另一个命令getevent,下面是我用nexus 4执行getevent得到的结果。这里我加了-l参数,加上这个参数在触摸屏幕时我们就可以看到

1|shell@mako:/ $ getevent -l
add device 1: /dev/input/event0
  name:     "pmic8xxx_pwrkey"
add device 2: /dev/input/event1
  name:     "keypad_8064"
add device 3: /dev/input/event4
  name:     "apq8064-tabla-snd-card Headset Jack"
add device 4: /dev/input/event3
  name:     "apq8064-tabla-snd-card Button Jack"
add device 5: /dev/input/event5
  name:     "hs_detect"
add device 6: /dev/input/event2
  name:     "touch_dev"

由于我们需要的是点击,可以看到device 6的name时touch_dev,看样子就是触摸屏的设备了。为了确认猜测,我们可以点击一下屏幕。看到如下输出:

/dev/input/event2: EV_ABS       ABS_MT_TRACKING_ID   000004e0            
/dev/input/event2: EV_ABS       ABS_MT_POSITION_X    000003d2            
/dev/input/event2: EV_ABS       ABS_MT_POSITION_Y    0000042c            
/dev/input/event2: EV_ABS       ABS_MT_PRESSURE      0000002e            
/dev/input/event2: EV_ABS       ABS_MT_TOUCH_MAJOR   00000003            
/dev/input/event2: EV_SYN       SYN_REPORT           00000000            
/dev/input/event2: EV_ABS       ABS_MT_TRACKING_ID   ffffffff            
/dev/input/event2: EV_SYN       SYN_REPORT           00000000            

这里我们看EV_ABS,ABS_MT_TRACKING_ID就是前面-l参数的功劳了。这些参数大概的意思是(纯猜测,没找到文档):ABS_MT_TRACKING_ID //是用来追踪一次点击的标识,每次点击累加,与最后的ABS_MT_TRACKING_ID和SYN_REPORT 形成一个完整的点击事件。第二个参数就是点击的x坐标,第三个是y坐标,第四个参数是压力值。第五个参数没搞懂,死六个是同步标识,第七个是结束标识,第八个是同步标识。
综上,我们要使用sendevent完成一次点击事件,至少要完成以上的一串输入。那么我们还要知道哪些标识具体的值,这时再次执行getevent不加-l参数。点击屏幕,看到如下的输出

/dev/input/event2: 0003 0039 000004eb
/dev/input/event2: 0003 0035 0000043d
/dev/input/event2: 0003 0036 000004bf
/dev/input/event2: 0003 003a 0000002e
/dev/input/event2: 0003 0030 00000003
/dev/input/event2: 0000 0000 00000000
/dev/input/event2: 0003 0039 ffffffff
/dev/input/event2: 0000 0000 00000000

得到这些值后,我们就可以组织我们的点击事件了,注意这里输出的都是16进制的,使用命令时要使用十进制的数字。这种方法不同的手机都不同,下面给出我适配过得一些设备:

HM NOTE 1LTEW
sendevent /dev/input/event2 3 57 710
sendevent /dev/input/event2 3 53 x
sendevent /dev/input/event2 3 54 y
sendevent /dev/input/event2 0 0 0
sendevent /dev/input/event2 3 57 4294967295
sendevent /dev/input/event2 0 0 0

HM NOTE 1LTE
sendevent /dev/input/event2 3 57 710
sendevent /dev/input/event2 3 53 x
sendevent /dev/input/event2 3 54 y
sendevent /dev/input/event2 0 0 0
sendevent /dev/input/event2 3 57 4294967295
sendevent /dev/input/event2 0 0 0

GT-I9500
sendevent /dev/input/event1 3 57 89
sendevent /dev/input/event1 1 330 1
sendevent /dev/input/event1 3 53 x
sendevent /dev/input/event1 3 54 y
sendevent /dev/input/event1 0 0 0
sendevent /dev/input/event1 3 57 4294967295
sendevent /dev/input/event1 0 0 0

MI 4LTE-CMCC
sendevent /dev/input/event2 3 57 79
sendevent /dev/input/event2 3 53 x 
sendevent /dev/input/event2 3 54 y
sendevent /dev/input/event2 3 58 60
sendevent /dev/input/event2 3 48 3
sendevent /dev/input/event2 1 330 1
sendevent /dev/input/event2 0 0 0
sendevent /dev/input/event2 3 58 71
sendevent /dev/input/event2 3 48 7
sendevent /dev/input/event2 0 0 0
sendevent /dev/input/event2 3 57 4294967295
sendevent /dev/input/event2 1 330 0
sendevent /dev/input/event2 0 0 0

MI 4W
sendevent /dev/input/event2 3 57 79
sendevent /dev/input/event2 3 53 x 
sendevent /dev/input/event2 3 54 y
sendevent /dev/input/event2 3 58 60
sendevent /dev/input/event2 3 48 3
sendevent /dev/input/event2 1 330 1
sendevent /dev/input/event2 0 0 0
sendevent /dev/input/event2 3 58 71
sendevent /dev/input/event2 3 48 7
sendevent /dev/input/event2 0 0 0
sendevent /dev/input/event2 3 57 4294967295
sendevent /dev/input/event2 1 330 0
sendevent /dev/input/event2 0 0 0

Coolpad 9080W
sendevent /dev/input/event3 3 57 0
sendevent /dev/input/event3 1 330 1
sendevent /dev/input/event3 3 53 x
sendevent /dev/input/event3 3 54 y
sendevent /dev/input/event3 0 2 0
sendevent /dev/input/event3 0 0 0
sendevent /dev/input/event3 1 330 0
sendevent /dev/input/event3 0 2 0
sendevent /dev/input/event3 0 0 0

HUAWEI MT7-TL00
sendevent /dev/input/event5 3 58 47
sendevent /dev/input/event5 3 53 x
sendevent /dev/input/event5 3 54 y
sendevent /dev/input/event5 3 57 0
sendevent /dev/input/event5 0 2 0
sendevent /dev/input/event5 1 330 1
sendevent /dev/input/event5 0 0 0
sendevent /dev/input/event5 0 2 0
sendevent /dev/input/event5 1 330 0
sendevent /dev/input/event5 0 0 0

Nexus 6
sendevent /dev/input/event0 3 57 10
sendevent /dev/input/event0 3 53 x
sendevent /dev/input/event0 3 54 y
sendevent /dev/input/event0 3 58 53
sendevent /dev/input/event0 3 48 6
sendevent /dev/input/event0 0 0 0
sendevent /dev/input/event0 3 58 29
sendevent /dev/input/event0 3 48 2
sendevent /dev/input/event0 0 0 0
sendevent /dev/input/event0 3 57 4294967295
sendevent /dev/input/event0 0 0 0

XT1085
sendevent /dev/input/event1 3 57 120
sendevent /dev/input/event1 3 53 x
sendevent /dev/input/event1 3 54 y
sendevent /dev/input/event1 3 58 50
sendevent /dev/input/event1 3 48 6
sendevent /dev/input/event1 0 0 0
sendevent /dev/input/event1 3 57 4294967295
sendevent /dev/input/event1 0 0 0

NX601J
sendevent /dev/input/event0 3 57 80
sendevent /dev/input/event0 3 53 x
sendevent /dev/input/event0 3 54 y
sendevent /dev/input/event0 3 58 74
sendevent /dev/input/event0 3 48 8
sendevent /dev/input/event0 3 49 8
sendevent /dev/input/event0 0 0 0
sendevent /dev/input/event0 3 53 x
sendevent /dev/input/event0 3 54 y
sendevent /dev/input/event0 3 58 74
sendevent /dev/input/event0 3 48 6
sendevent /dev/input/event0 3 49 6
sendevent /dev/input/event0 0 0 0
sendevent /dev/input/event0 3 57 4294967295
sendevent /dev/input/event0 0 0 0

3 、使用jni写事件

方法2中我们使用了sendevent命令进行点击。翻看安卓的sendevent源码我们可以看到,其实现就是对event文件进行写入操作。那么我们也可以做同样的事情。具体的jni代码实现如下。在实现中,为了实现快速的点击,我只是第一次写事件时进行文件的打开操作,之后都用同一个文件句柄进行写入。用完了之后再通过jni接口释放掉该句柄。

#include <jni.h>
#include <fcntl.h>
#include <linux/input.h>
#include <stdlib.h>

extern "C" {
    JNIEXPORT int JNICALL Java_sogou_test_kpi_base_AndroidToCPP_sendevent(JNIEnv * env, jobject obj,int argc, jobjectArray strArray,int fd);
    JNIEXPORT int JNICALL Java_sogou_test_kpi_base_AndroidToCPP_closefilehandler(JNIEnv * env, jobject obj,int fd);
}
//static public int fd = 0;
JNIEXPORT int JNICALL Java_sogou_test_kpi_base_AndroidToCPP_sendevent(JNIEnv * env,jobject obj,int argc, jobjectArray strArray,int fd)
{
    jstring jstr;
    jsize len = argc;
    char **argv = (char **) malloc(len*sizeof(char *));

    jsize i=0;

    for (i=0 ; i<len; i++) {
        jstr = (jstring)env->GetObjectArrayElement(strArray, i);
        argv[i] = (char *)env->GetStringUTFChars(jstr, 0);

    }
    argc = len;
//      int fd;
        ssize_t ret;
        int version;
        struct input_event event;

        if(argc != 5) {
//          close(fd);//fprintf(stderr, "use: %s device type code value\n", argv[0]);
            return 1;
        }

        if(fd == 0) {
            fd = open(argv[1], O_RDWR);
        }
        if(fd < 0) {
            close(fd);//fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
            return -2;
        }
        if (ioctl(fd, EVIOCGVERSION, &version)) {
//          close(fd);//fprintf(stderr, "could not get driver version for %s, %s\n", argv[optind], strerror(errno));
            return -3;
        }
        memset(&event, 0, sizeof(event));
        event.type = atoi(argv[2]);
        event.code = atoi(argv[3]);
        event.value = atoi(argv[4]);
        ret = write(fd, &event, sizeof(event));
        if(ret < (ssize_t) sizeof(event)) {
//          close(fd);
            //fprintf(stderr, "write event failed, %s\n", strerror(errno));
            return -1;
        }
//      close(fd);

    return fd;
}


JNIEXPORT int JNICALL Java_sogou_test_kpi_base_AndroidToCPP_closefilehandler(JNIEnv * env,jobject obj,int fd)
{
//  fd
    if(fd>0)
        close(fd);

    return 0;
}

参考:
https://github.com/android/platform_system_core/blob/master/toolbox/sendevent.c

4、使用monkey进行点击

通常我们用monkey都是用来做压力测试,而monkey是在pc上执行的shell命令。所以monkey执行的点击必然也是通过把点击事件传给系统内某个进程实现的。这个进程就是

com.android.commands.monkey

首先我们需要启动这个进行,在shell下可以通过如下命令启动

monkey --port 3131

要与这个进程进行通信需要用到socket,只需要通过回路ip 127.0.0.1与端口3131就能与monkey进程建立通信。之后就可以发送点击命令,monkey里的点击分为down和up两部分。发送

touch down x y
touch up x y

完成一次点击操作。也可以发送系统keycode,可以从
http://developer.android.com/reference/android/view/KeyEvent.html#FLAG_FALLBACK
找到具体的值。

以上总结了用于安卓点击的四种方法,如果要在android直接执行都需要root权限,方法四也可以在pc上把monkey服务启动,安卓上就不需要root了。

当然还可以用谷歌提供给的自动化方法Instrumentation和UiAutomator实现点击。不过Instrument只能在被测进程点击。且这两个主要用于自动化测试。如果要集成到测试工具里做些简单的随机的点击不是很方便,需要提前编写脚本。

推荐阅读更多精彩内容