Android 脚本实时模拟行为

在使用 Android 应用的时候,我们经常需要进行一些自动化的操作,比如自动点击某些按键,自动输入一些符号,自动玩网络游戏等。这篇文章是希望备忘一些这方面的技术。

第一种需要进行模拟的情况是 chaos testing 等自动化测试技术。假设我们有现在做了一个含有 UI 的应用需要测试稳健性,我们会希望生成大量行为来确保大多数行为不会造成未知的后果。在这种情况下,使用 Appium, Culebra 是不错的选择。这类 UI tester 会采用各种技术检测当前屏幕上的元素,从而可以穷尽式地依次检查元素功能。其中 Culebra 甚至还有 GUI,使用门槛为0,不再多介绍。

由于自动测试对模拟操作的要求很低,我们现在不打算继续讨论自动测试。实际上,写这篇文章的直接原因是对之前开发的一些手机网络游戏 AI 的操作封装部分的总结。在网络游戏 AI 中,实时性非常重要,因此,我们希望所有模拟按下、滑动、放开等操作是几乎无延迟的,并且必须支持 multitouch。

最容易想到的技术是直接调用 adb shell。事实上,在去年的微信跳一跳AI和冲顶大会AI中这种技术就被大量使用了。

adb shell input 支持多种操作,touch, swipe, drag & drop等。比方说,如果我们采用 python 脚本,可以直接在拼装指令后利用 subprocess.popen 生成操作。

然而,上面这种操作每次都要重新生成 adb 子进程,不适合高实时反馈。因此,一个简单的改进是将 adb shell 反射出来,从而每次调用 input 即可。

然而,adb shell 的方法存在其本质的问题,例如难以实现复杂操作(拐弯、画圆等),难以完成 multitouch 动作(需要直接操作底层 event)等。因此,另一个办法是使用 android_sdk 自带的 monkeyrunner。

monkeyrunner 提供一个 jython API,其用法与传统 python 模组基本相同,我们常用的基本操作是 device.touch(x,y,”DOWN”), device.touch(x,y,”MOVE”) 和 device.touch(x,y,”UP”)。利用这些操作加上合适的延时设置,可以得到效率不错的模拟操作。

对于单点操作的 AI,使用 monkeyrunner 是一个比较理想的做法,当然由于我们不喜欢过时的 py2,并且希望用传统的 cpython 而不是 jython,一般来说我会将 monkeyrunner 再封装一层,暴露出 RPC 端口以供调用。

monkeyrunner 的问题是它同样不支持 multitouch,这对一些半操作性的场合是不太友善的,当然我们可以用离散的反复操作来处理像是吃鸡、王者荣耀这些情况,但是这样写出来的代码会非常难看。

为了完全支持 multitouch 并且保存高实时性,我们会需要向 android 设备上推送 binary 并且执行。这是目前为止最为稳定和高效的办法,并且对另一边 AI 的输出部分编程也会变得更容易。

一个现成的 library 是 openstf 的 minitouch。这里我们可以着重介绍一下这个 lib。

首先我们 build 这个 lib。

git clone https://github.com/openstf/minitouch
cd minitouch
git submodule init
git submodule update
ndk-build

运行时,我们首先检测设备的 CPU 架构,根据架构推送 binary 到临时目录

ABI=$(adb shell getprop ro.product.cpu.abi | tr -d '\r')
adb push libs/$ABI/minitouch /data/local/tmp/

接下来就可以直接启动程序并且监听 socket

adb shell /data/local/tmp/minitouch

在本地 forward 到端口 10000

adb forward tcp:10000 localabstract:minitouch

然后我们就可以对这个 socket 为所欲为了,

nc localhost 10000

更多文档在 https://github.com/openstf/minitouch

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.