本文共 4131 字,大约阅读时间需要 13 分钟。
本文不涉及如何使用,官网说得够详细了,网上资料也一大堆。本文着重探讨monkey的实现原理,以及基于这些原理,我们可以做些什么?本文涉及的Monkey的源码位于AOSP的项目的cmds目录下。
Monkey的主要作用是,发送一些随机交互事件模拟人的随机操作。它是如何触发各种事件的呢?
触摸事件包括屏幕以及物理键的触摸,滑动,点击事件。我们可以通过发送一些adb命令模拟这些事件。不过Monkey实现时,直接调用hiden API实现。
InputManager.getInstance().injectInputEvent(keyEvent, int)
构造需要的事件,然后调用该接口就能触发了。
Activity事件是指我们调用Android系统组件的事件。一般测试中,我们也是通过adb shell am命令来实现的。Monkey在实现是,是直接通过IActivityManager实例:
//同样通过hiden API获得IActivityManager实例IActivityManager am = ActivityManagerNative.getDefault();//利用IActivityManager接口打开Activityam.startActivity();
Window事件是指操作Window的事件,例如转屏。这里直接用IWindowManager实现。
//获得IWindowManager实例IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));//调用IWindowManager接口转屏wm..thawRotation();
此外还会调用其他的一些hiden API获取崩溃信息,ANR信息等等。这些直接从底层hiden接口拿,比我们上层获取更加方便,准确。
上一节分析到Monkey的功能主要分成两块:
因此它的框架设计非常简洁,核心类是Monkey.java, MonkeyEventSource.java, MonkeyEvent.java。
核心类 | 说明 |
---|---|
Monkey | 程序的入口,同时也是调度中心,根据参数选择合适的MonkeyEventSource,并适时触发MonkeyEvent。 |
MonkeyEventSource | MonkeyEvent的工厂,是一个接口。它有各种实现,例如随机生成MonkeyEvent,根据配置文件生成MonkeyEvent,根据网络数据生成MonkeyEvent等等。 |
MonkeyEvent | 各种事件的具体实现,是一个抽象类,不同事件有不同实现。在Monkey中各种活动都是事件,除了基本的触摸事件,Activity事件外,事件之间的停顿也是通过一个MonkeyThrottleEvent来实现。这样概念的扩展,将各种活动一视同仁的对待,使设计变得简单。 |
如果我们需要扩展Monkey的功能,只需要增加自己实现的MonkeyEventSource和MonkeyEvent即可。
我们在terminal中执行adb shell monkey,实际上是执行手机中/system/bin/monkey这个脚本(在源码中也能看到),该脚本具体内容是
# Script to start "monkey" on the device, which has a very rudimentary# shell.#base=/systemexport CLASSPATH=$base/framework/monkey.jartrap "" HUPexec app_process $base/bin com.android.commands.monkey.Monkey $*
所以他是通过/system/bin/app_process运行/system/framework/monkey.jar。
app_process的核心源码是它会新建一个native进程,初始化虚拟机,从CLASSPATH找到我们定义的Main Class,并把参数传给它。可以参考。根据上一节的分析,可以通过exec app_process直接执行一个jar包。所以理论上我们可以自己写一个jar包,丢给它执行。可以参考一下。
这里需要注意,JVM中的jar文件是一对class文件的zip包。而在Android中,可执行文件是dex,所以Android里面的jar本质上里面也是dex,直接把eclipse导出的jar包放进去是无法执行的。
这里也有一个小例子(可以在附件中下载完整代码):
/** * Android可执行jar测试。 * 测试程序会每隔三秒钟写一次文件,报告自己还活着,可以测试该程序的运行时长。 * * 使用方式: * 1. 利用android_jar.sh一键生成一个dex文件test.dex; * 2. 将testJar放到/data/local/tmp/目录下,并授予执行权限; * 3. 运行testJar文件。 * Created by shangjie on 2016/11/9. */public class Monkey { public static void main(String[] args) { Process.setArgV0("io.light.monkey"); System.out.println("Process start"); Monkey main = new Monkey(); main.run(); System.exit(0); } public void run() { File file = Environment.getExternalStorageDirectory(); file = new File(file, "ali_jar_process.txt"); if (file.exists()) { boolean isDelete = file.delete(); println("Delete file: " + isDelete); } try { boolean result = file.createNewFile(); println("Create file: " + result); } catch (IOException e) { e.printStackTrace(); } SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HH:mm:ss"); int i = 0; while (i < 900) { String time = sdf.format(new Date()); String content = time + " I am still live."; println(content); writeFile(file, content); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } i ++; } } void writeFile(File file, String mValue) { try (BufferedWriter bw = new BufferedWriter(new FileWriter(file, true))) { bw.newLine(); bw.write(mValue); bw.flush(); } catch (Exception e) { e.printStackTrace(); } } static void println(String msg) { System.out.println(msg); }}
我可以看到它成功运行了:
进一步分析该进程,通过ps命令,我们看到该进程是属于shell用户组的,因此它没有root,system那样高的权限,但是比一般的用户进程权限也要高很多,可以多做很多事情,例如静默地安装,卸载。
通过查看该进程的oom_adj文件,我们知道他的ADJ值是-17,根据源码-17是NATIVE_ADJ是不受系统控制的,理论上不会被系统杀掉,可以一直运行,不过我自己的测试中,发现还是会die,还在分析原因。从我们直观经验中,monkey,UiAutomator的确可以运行很久。
上述Monkey的分析同样适用于UiAutomator等Android内部的测试工具,更重要的是,我们可以基于这些原理实现我们自己的功能强大,且稳定可靠的测试工具。
转载地址:http://pxxpl.baihongyu.com/