在《MonkeyRunner源码分析之与Android设备通讯方式》中,我们谈及到MonkeyRunner控制目标android设备有多种方法,其中之一就是在目标机器启动一个monkey服务来监听指定的一个端口,然后monkeyrunner再连接上这个端口来发送命令,驱动monkey去完成相应的工作。

当时我们只分析了monkeyrunner这个客户端的代码是怎么实现这一点的,但没有谈monkey那边是如何接受命令,接受到命令又是如何处理的。

所以自己打开源码看了一个晚上,大概有了概念。但今天网上搜索了下,发现已经有网友“chenjie”对monkey的源码做过相应的分析了,而且文章写得非常有概括性,应该是高手所为,果断花了2个积分下载下来,不敢独享,本想贴上来分享给大家,但发现pdf的文档直接拷贝上来会丢失掉图片,所以只好贴上下载地址:http://download.csdn.net/download/zqilu/6884491

但文章主要是架构性得去描述monkey是怎么工作的,按照我自己的习惯,我还是喜欢按照自己的思维和有目的性的去了解我想要的,在这里我想要的是搞清楚monkey是如何处理monkeyrunner过来的命令的。

本文我们就先看下monkey的运行流程。

1. 运行环境设置

和monkeyrunner一样,monkey这个命令也是一个shell脚本,它是在我们的目标android设备的“/system/bin/monkey”,其实这是一个android上面java程序启动的标准流程。

base=/system
export CLASSPATH=$base/framework/monkey.jar
trap "" HUP
exec app_process $base/bin com.android.commands.monkey.Monkey $*

android中可以通过多种方式启动java应用,通过app_process命令启动就是其中一种,它可以帮忙注册android JNI,而绕过dalvik以使用Native API(如我般不清楚的请百度)所做的主要事情如下:

  • 设置monkey的CLASSPATH环境变量指向monkey.jar
  • 通过app_process指定monkey的入口和传进来的所有参数启动上面CLASSPATH设定的monkey.jar
 

2.命令行参数解析

通过以上的app_process指定的monkey入口,我们可以知道我们的入口函数main是在com.android.commands.Monkey这个类里面的:
    /**
* Command-line entry point.
*
* @param args The command-line arguments
*/
public static void main(String[] args) {
// Set the process name showing in "ps" or "top"
Process.setArgV0("com.android.commands.monkey"); int resultCode = (new Monkey()).run(args);
System.exit(resultCode);
}
入口函数很简单,直接跳到run这个方法,这是一个很重要的方法,里面大概会做以下这些事情:
  • 处理命令行参数
  • 根据命令行参数启动不同的事件源,也就是我们的测试事件究竟是从网络如monkeyrunner过来的还是monkey内部的random测试数据集过来的还是脚本过来的如此之类
  • 跳入runMonkeyCyncle方法针对不同的事件源开始获取并执行不同的事件
这个方法会比较长,我们只看我们现在想要的关键片段,可以看到里面调用了命令行处理函数processOptions.
    private int run(String[] args) {
...
if (!processOptions()) {
return -1;
}
...
}

进去之后就是很普通的读取命令行的参数然后一个个进行解析保存了,没有太多特别的东西,这里就直接贴出monkey的参数选项大家看看就好了:

3. 初始化测试事件源

如前所述,run方法里面在获得命令行参数后会进入下一个环节,就是根据不同的参数去初始化不同的事件源
   private int run(String[] args) {
...
if (mScriptFileNames != null && mScriptFileNames.size() == 1) {
// script mode, ignore other options
mEventSource = new MonkeySourceScript(mRandom, mScriptFileNames.get(0), mThrottle,
mRandomizeThrottle, mProfileWaitTime, mDeviceSleepTime);
mEventSource.setVerbose(mVerbose); mCountEvents = false;
} else if (mScriptFileNames != null && mScriptFileNames.size() > 1) {
if (mSetupFileName != null) {
mEventSource = new MonkeySourceRandomScript(mSetupFileName,
mScriptFileNames, mThrottle, mRandomizeThrottle, mRandom,
mProfileWaitTime, mDeviceSleepTime, mRandomizeScript);
mCount++;
} else {
mEventSource = new MonkeySourceRandomScript(mScriptFileNames,
mThrottle, mRandomizeThrottle, mRandom,
mProfileWaitTime, mDeviceSleepTime, mRandomizeScript);
}
mEventSource.setVerbose(mVerbose);
mCountEvents = false;
} else if (mServerPort != -1) {
try {
mEventSource = new MonkeySourceNetwork(mServerPort);
} catch (IOException e) {
System.out.println("Error binding to network socket.");
return -5;
}
mCount = Integer.MAX_VALUE;
} else {
// random source by default
if (mVerbose >= 2) { // check seeding performance
System.out.println("// Seeded: " + mSeed);
}
mEventSource = new MonkeySourceRandom(mRandom, mMainApps, mThrottle, mRandomizeThrottle);
mEventSource.setVerbose(mVerbose);
// set any of the factors that has been set
for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
if (mFactors[i] <= 0.0f) {
((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
}
} // in random mode, we start with a random activity
((MonkeySourceRandom) mEventSource).generateActivity();
}
...
mNetworkMonitor.start();
int crashedAtCycle = runMonkeyCycles();
mNetworkMonitor.stop();
...
}

事件源代表测试数据的事件是从哪里过来的,不同的event source会有不同的类来做相应的实现:

  • MonkeySourceNetwork.java: 事件是从网络如monkeyrunner过来的,处理的是《MonkeyRunner源码分析之与Android设备通讯方式》描述的界面控制操作事件
  • MonkeySourceNetworkVars.java: 事件也是从网络如monkeyrunner过来的,处理的是《MonkeyRunner源码分析之与Android设备通讯方式》提到的getPropery事件
  • MonkeySourceNetworkViews.java:事件也是从网络如monkeyrunner过来的,处理的是《MonkeyRunner源码分析之与Android设备通讯方式》提到的Views相关的事件
  • MonkeySourceRandom.java:事件是从monkey内部生成的随机事件集,也就是我们通过命令行启动monkey测试目标app的常用方式
  • MonkeySourceRanodomeScript.java: 上面的随机内部数据源也可以通过指定setup脚本来创建
  • MonkeySourceScript.java: 用户也可以遵循一定的规则编写monkey脚本来驱动monkey进行相关测试,与上面不同的是它不再是随机的
往后的文章我们会针对其中一个事件源进行分析,在这里我们只需要知道这些事件源代表了事件的不同的来源,它会
  • 从指定的源获取命令
  • 把命令翻译成monkey事件然后放到命令队列EventQueue
这里需要注意的是每一个EventSource类都是实现了MonkeyEventSource这个接口的,这个接口最重要的就是要求实现类必须实现getNextEvent这个方法来生成并获取事件。这样子做的好处就是屏蔽了每一个具体事件源的实现细节,其他地方的代码只需要调用这个接口的getNextEvent方法获得事件源就行了,而无需关心这些事件是从哪个源过来的。这些都是面向对象的面向接口编程的基础了,大家有不清楚的最好先去了解下java的一些基本知识,这样理解起来会快很多。
 

4. 循环执行事件

run方法根据参数从不同的事件源获得事件并放入到EventQueue后,就会开始执行一个循环去从EventQueue里获取事件进行执行
    private int run(String[] args) {
...
int crashedAtCycle = runMonkeyCycles();
...
}

如前所述,runMonkeyCyles方法会根据不同的数据源开始一条条的获取事件并进行执行:

    /**
* Run mCount cycles and see if we hit any crashers.
* <p>
* TODO: Meta state on keys
*
* @return Returns the last cycle which executed. If the value == mCount, no
* errors detected.
*/
private int runMonkeyCycles() {
int eventCounter = 0;
int cycleCounter = 0; boolean shouldReportAnrTraces = false;
boolean shouldReportDumpsysMemInfo = false;
boolean shouldAbort = false;
boolean systemCrashed = false; // TO DO : The count should apply to each of the script file.
while (!systemCrashed && cycleCounter < mCount) {
...
MonkeyEvent ev = mEventSource.getNextEvent();
if (ev != null) {
int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
...
}
...
}
....
}

注意这里的mEventSource就是我们上面提到的事件源的接口,它屏蔽了每个事件实现类的具体细节,我们只需要告诉这个接口我们现在需要取一条事件然后执行它,该结构根据面向对象的多态原理,就会自动取事件的实现类获得对应的事件进行返回。

所以这里大家还需要对多态这个概念有所了解,特别是一些从手动测试转到自动化测试的朋友,可能之前没有接触过太多面向对象的知识。本人以前做过开发,所以还ok。这里只是做一个善意的提醒。
获得事件后下一步就是去执行相应的事件了,不同的事件会有不同的处理方式,或只是执行个命令,或调用WindowManager隐藏接口做事件注入等,这些都会在今后文章进行进一步阐述
 
这一篇文章就到此为止了,目的就是让大家对整一个monkey执行的流程有个初步的了解,方便理解往下的相关文章。
 
作者 自主博客 微信第一手行业资讯分享 CSDN
天地会珠海分舵 http://techgogogo.com

服务号:TechGoGoGo

扫描码:

http://blog.csdn.net/zhubaitian

安卓Monkey源码分析之运行流程的更多相关文章

  1. [原创]java WEB学习笔记70:Struts2 学习之路-- struts2拦截器源码分析,运行流程

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  2. 安卓MonkeyRunner源码分析之工作原理架构图及系列集合

    花了点时间整理了下MonkeyRunner的工作原理图,请配合本人博客里面MonkeyRunner其他源码分析文章进行阅读.下面整理成相应系列列表方便大家阅读: MonkeyRunner源码分析之-谁 ...

  3. Monkey源码分析之事件源

    上一篇文章<Monkey源码分析之运行流程>给出了monkey运行的整个流程,让我们有一个概貌,那么往后的文章我们会尝试进一步的阐述相关的一些知识点. 这里先把整个monkey类的结构图给 ...

  4. Monkey源码分析之事件注入

    本系列的上一篇文章<Monkey源码分析之事件源>中我们描述了monkey是怎么从事件源取得命令,然后将命令转换成事件放到事件队列里面的,但是到现在位置我们还没有了解monkey里面的事件 ...

  5. openVswitch(OVS)源码分析之工作流程(哈希桶结构体的解释)

    这篇blog是专门解决前篇openVswitch(OVS)源码分析之工作流程(哈希桶结构体的疑惑)中提到的哈希桶结构flex_array结构体成员变量含义的问题. 引用下前篇blog中分析讨论得到的f ...

  6. monkey源码分析之事件注入方法变化

    在上一篇文章<Monkey源码分析之事件注入>中,我们看到了monkey在注入事件的时候用到了<Monkey源码分析番外篇之Android注入事件的三种方法比较>中的第一种方法 ...

  7. nodejs的Express框架源码分析、工作流程分析

    nodejs的Express框架源码分析.工作流程分析 1.Express的编写流程 2.Express关键api的使用及其作用分析 app.use(middleware); connect pack ...

  8. Okhttp源码分析--基本使用流程分析

    Okhttp源码分析--基本使用流程分析 一. 使用 同步请求 OkHttpClient okHttpClient=new OkHttpClient(); Request request=new Re ...

  9. 安卓MonkeyRunner源码分析之启动

    在工作中因为要追求完成目标的效率,所以更多是强调实战,注重招式,关注怎么去用各种框架来实现目的.但是如果一味只是注重招式,缺少对原理这个内功的了解,相信自己很难对各种框架有更深入的理解. 从几个月前开 ...

随机推荐

  1. 腾讯2014在广州站实习生offer经验(TEG-开发背景)

    研究在过去的一年是linux 什么系统编程和网络编程.比较熟悉的语言c/c++,python只写一些测试client.是后台开发类,比方前面笔面的网易CC(面完hr后挂).大概3月15号就在腾讯 jo ...

  2. iPhone&amp;iPad DFU及恢复模式刷机、降级教程

    再次提醒,刷机需慎重处理. http://blog.csdn.net/ztp800201/article/details/11980643 iphone一共同拥有三种工作模式,各自是正常模式,恢复模式 ...

  3. Debian/Ubuntu 已安装gcc/g++ 4.8.1

    gcc 4.8.1 是第一个全然支持C++11(C++14非常可能在gcc 4.9.0開始支持.)的编译器,Windows上能够安装mingw版的.在sourceforge 上有下载.安装也比較方便. ...

  4. MKMapView移动事件地图

    MKMapView移动事件地图 by 吴雪莹 -(void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated { ...

  5. SQL入门学习0-数据库与SQL

    1.1 DBMS DatabaseManagermentSystem 数据库管理系统 DBMS种类 层次型数据库(HDB) 最古老的数据库之一,把数据通过层次结构的方式表现. 关系型数据库(RDB) ...

  6. NSIS:安装、卸载时检查程序是否正在运行

    原文 NSIS:安装.卸载时检查程序是否正在运行 如果我们要安装或升级的程序正在运行,文件肯定会替换不成功,以下代码可以提示用户结束正在运行的程序. 需要使用插件FindProcDLL.dll,下载路 ...

  7. Android通过意图使用内置的音频播放器

    假设实现一个音频文件的播放,那么在应用程序中提供播放音频文件功能的最简单的方式是利用内置的"Music(音乐)"应用程序的功能--即使用系统自带的或已安装好的音乐播放器来播放指定的 ...

  8. WebAPI上传大文件

    今天在研究WebAPI的上传与下载,作为Rest的框架,更多是面向资源,就其本身来说,是不会涉及也不应该涉及到大文件的处理,具体多大呢,也就是ASP.NET的限值2G. ASP.NET的pipelin ...

  9. 选择一个利于SEO的空间

    大家好.今天蜗牛将给大家分享怎么选择一个利于SEO的站点空间. 一.什么是站点空间? 站点空间,是用来存你的站点的HTML,.图片.文件等的一个远程硬盘空间(就像你的电脑里面的空间一回事儿).依据不同 ...

  10. 在ASP.net中的UpdatePanel,弹窗失败解决办法

    原文:在ASP.net中的UpdatePanel,弹窗失败解决办法 最开始我用: Response.Write("<script>alert('和哈呵呵呵呵呵呵!')</s ...