写一些测试工具经常用到点击。本文总结了一些跨进程点击的几种方法。

由于要跨进程注入,所以本文讲的方法都是需要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 
#include 
#include 
#include 
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
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;
}

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

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

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