android在adb下模拟长按事件

android在adb下模拟长按事件

我们都知道,android系统能出发多种事件,包括点击滑动,长按等等。

熟悉android应用或者系统开发的人知道,一个长按事件有什么难的,但是请你注意,是在adb下模拟,这与我们使用java代码实现有何不同呢,当然,你百度,google android长按事件,你会搜索到很多java实现的代码。既然这么多实现方法,那我今天讲解这个adb下模拟长按事件有何意义呢。

举个例子:现在很多市面上有很多手机,按住home键可以拍照,或者按住某些组合键就可以调出相机,短信等等,可能你看了这篇文章,你也会自定义你自己的一些按钮(android手机)。

此外,做过软件开发的人都知道,多数的软件开发后都会有压测,android系统也不例外,如果你正在做android系统克制化,甚至在克制化一些按键,那么你可能就会去压力测试这些按键是否健壮,你不可能让一个人一直重复的去按某个键或某几个键吧,因为压测一般都会持续1天到数天不等,你也可以写一个apk或者进程去压测,不过这样的扩展不够灵活,复用度不高,此时就会想到使用shell脚本去模拟这些按钮,如果想更换压测按钮只需更改其中的键值即可。

废话不多说,接下来我将讲解三种模拟长按的方式:

第一种:在adb下使用 input keyevent --longpress <键值>

比如长按home键 可以使用:input keyevent --longpress KEYCODE_HOME ,KEYCODE_HOME就是android为home键定义的键值,它是一个宏,其实是一个数字。

当然你也可以使用数字:input keyevent --longpress 3 键值3对应的宏便是KEYCODE_HOME,也能模拟长按home键。android中还有很多键值,一个数字键,字母键,我们不可能记住这么多数字,所以还是推荐使用第一种宏的方式。这些宏对应的键值网上与很多对应表我就不一一列举了,这个就比较全请参考:Android KEYCODE键值对应大全

那这种长按的方式有什么缺点呢:长按时间太短。比如我们要模拟长按电源键呼出关机菜单或者长按切换应用键呼出分屏(android7.0新功能),这种方式长按时间太短。只适用与拖动选择时一开始的长按情况。

第二种:在adb下使用input swipe <x1> <y1> <x2> <y2> [duration(ms)]

上面这条指令实际上是滑动的指令,x1,y1和x2,y2分别是两个坐标,duration是从x1,y1处滑动到x2,y2处规定的时间。为什么滑动可以模拟长按呢:只要我们两个坐标之间的差值足够小,还在当前这个按钮的范围内,android系统就会认为我们进行了长按某个按钮的操作,当然这只能模拟虚拟按钮,因为实体键我们是没法获取到它的坐标的。

那么什么情况才能满足坐标足够小呢,就我看来只需要 在原坐标值上加1即可:

input swipe <x1> <y1> <x1+1> <y1+1> [duration(ms)],由于1在屏幕中占的位置很小,肯定不会超过一般按钮的大小,因此只要定位准确按钮的坐标位置,这个方法就可以实现长按某个虚拟按钮。那么如何确定某个按钮的坐标位置成为关键。

android 提供了一个方法用于获取来自上层发送到驱动的input事件的方法——getevent,这个方法会去获取android input设备文件上的键值和坐标值。我们只需要在adb下运行这个命令,即可获取我们当前点击按钮的坐标值。建议你将这个功能运行在后台 只需要运行getevent & 这条指令,每次我们手机或者平板上的任意一点,都会打出对应的坐标值。如图便是我点击home键打印的坐标情况,当然如果你按了实体键,将会打印出键值,而不是坐标,接下来我会讲。


add device 1: /dev/input/event0: 代表 一个设备文件,用以标志一个输入设备,当然这样的输入设备文件可以有多个,分别对应不同的设备驱动,比如androidTV可能有遥控器,键盘等等。由于我这里使用的是模拟器,只有一个设备文件。

第一列:/dev/input/event0 就代表的不同device收到了一个事件

第二列:0001 代表一个type

第三列:如果是点击屏幕上的虚拟按键,这个值是014a用以标识点击了屏幕。如果是0代表x值,1代表y值,也是16进制。其他值代表键值。

第四列:00000001 代表value 一般 1代表按下,0代表放开。其它值则代表坐标(虚拟按键)

如此我们就可以看到,点击一次屏幕,就会打印出点击这个点的坐标值,我们就将该坐标值转化为10进制,在使用swipe命令就可以实现长按了,比如上图中的home键长按即可表示为:

input swipe 546 1860 547 1861 2000 就这么简单,赶紧去试一试吧。

这种方法虽然能够控制长按的时间,不过只能模拟虚拟按键的情况。如果想模拟实体键,比如android TV遥控器上就会有很多实体键。此时就要使用第三中方案。

第三种:sendevent device type code value

既然有getevent获取input事件,那么就肯定会有sendevent 发送事件,同样的道理,这个方法就会向设备文件发送特定的键值,从而驱动能将改键值上报给应用或者是系统。举一个简单例子:sendevent /dev/input/event0 1 213 1 该命令是向设备文件/dev/input/event0 发送了一个键值为213的按下(down)操作。sendevent /dev/input/event0 1 213 0 命令则是发送一条指令给设备文件,让213这个键松开(up)。这样一按一放,我们中间只要让其睡几秒即可实现长按操作。那么关键是如何确定这个按键的键值,这里要声明的是这个键值和我们方案一中提到的类似KEYCODE_HOME这样的宏定义个值不一样,之后我会将为什么,因此你会发现当你用方案一中 android键值映射表中键值来使用 sendevent的时候不起作用。那么如何找到这个键值是关键。

1、你可以使用第二个方案中提供的getevent方法获取这个键值,即你按一个实体键依然会打印实体键的键值。下图便是我按下power键后打印的键值:

0074便是power键的键值,转化为10进制为116,因此你现在就可以大胆的实现,你长按power键的共能了,只需要使用如下脚本即可:

#!/bin/sh
sendevent /dev/input/event0 1 116 1
sendevent /dev/input/event0 0 0 0
echo "down"
sleep 3
sendevent /dev/input/event0 1 116 0
sendevent /dev/input/event0 0 0 0
echo "up"

此时即可模拟长按power键的效果:

那万一我们手机或者当时没有这个按键该怎么办呢,比如我要模拟一个游戏手柄上的按键,而此时我又没有游戏手柄,该怎么获取这些键值呢,这就涉及到第二种获取键值的方式。

2、在android系统中作为键值会有两次映射,第一次是将驱动的键值映射到系统,再由系统framework将其映射为我们使用的键值(即我们方案一中提到的那些宏KEYCODE_HOME),作为程序员,我们不希望我们每次使用的键值都不一样,因此android系统将底层抛出的键值再做一次映射确保framework和应用使用的键值保持一致,程序员写应用的时候再也不用担心不同厂家生产的手机或者电视使用的键值不同,因为android系统已经定义好标准了。比如home键键值就是3,无论你的应用在任何平台使用都不会出现这个3的键值变成其他的键。

然而不同的厂商会定义自己特殊的键,这些键又是在哪里去定义呢,android系统提供了一个映射文件,你可以使用 dumpsys input 去查找这个文件。如图:

图中显示了两个.kl文件即是映射文件。分别是:

/system/usr/keylayout/qwerty.kl和/system/usr/keylayout/Generic.kl这两个文件,Generic.kl是默认文件,如果厂商没有其他的克制化.kl文件,系统就会默认使用这个文件,因此我们就需要在/system/usr/keylayout/qwerty.kl文件中去寻找我们的底层驱动使用的键值。如图:

进入/system/usr/keylayout目录,你会看到很多.kl文件,那就是不同厂家克制化的键值对应关系,上图中就可以看到我的设备驱动中使用的键值对应关系了,我们可以看到POWER键对应到驱动的键值就是116,所以你理解了为什么使用getevent获取的键值是16进制的0066了吧,因为在驱动或者设备文件使用的是/system/usr/keylayout/qwerty.kl(不同厂家不一样,具体你可以使用dumpsys input查看)中的键值。这样既能够满足厂家的克制化需求,也能满足上层应用对键值的要求,是不是很合理呢。

此时如果你有兴趣当一个打破常规的人,你可以将CAMERA的键值也改成116,此时你就可以使用power键拍照了,你也可以把home键改成锁屏键,不仅如此你还可以自己添加自己想要的一些按键,自定义按键,只需要你的手机root后,连上adb,修改这个文件,重启手机即可,是不是很炫酷,赶紧试试吧。

编辑于 2017-04-07

文章被以下专栏收录