第8章2节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-解析处理命令行参数

MonkeyRunnerStarter是MonkeyRunner启动时的入口类,因为它里面包含了main方法.它的整个启动过程主要做了以下几件事情:

  • 解析用户启动MonkeyRunner时从命令行传输进来的参数: 因为MonkeyRunner需要根据指定的参数才能做事情,比如输入的一个需要执行的脚本。如果确实不知道任何参数的话它就会进入MonkeyRunner的交互模式,其实就是Jythong的交互模式,让用户可以边写代码边执行
  • 启动AndroidDebugBridge: 其实就是启动ADB服务器,因为MonkeyRunner跟设备通信的一个很重要的方法之一就是通过向ADB服务器发送命令来请求目标设备的服务
  • 启动设备监控线程: 其实这个是在启动AndroidDebugBridge的时候一并启动的。设备监控线程主要做的事情就是取监控设备是否有接入进来或者移除出去,如果有新的设备连接进来,或者说设备变成ONLINE状态(一个设备有多个状态:ONLINE|OFFLINE|RECOVERY|UNAUTHORIZED),那么就需要取监控设备里面的每个可调试进程,这主要是用来给DDMS等调试工具使用的。它维护了一个最新的设备列表
  • 启动AndroidDebugBridge:
  • 启动Monkey:
  • 运行测试脚本:

本小节我们会先去看下MonkeyRunner在启动的时候是如何获得命令行参数并对其进行解析处理的。整个过程其实跟monkey在启动的时候的命令行参数分析类似。往下我们先看下牵涉到的关键类之间的关系:

图8-2-1  MonkeyRunnerStarter类图

从类图中我们看到MonkeyRunnerStarter持有了一个MonkeyRunnerOptions类型的成员变量options,这个实例保存的就是解析出来的命令行参数,同时该类会提供一个processOptions方法来专门解析命令行参数。

我们先进入到MonkeyRunnerStart这个类的main方法:

178   public static void main(String[] args) {
179     MonkeyRunnerOptions options =
MonkeyRunnerOptions.processOptions(args);
180
181     if (options == null) {
182       return;
183     }
184
185
186     replaceAllLogFormatters(MonkeyFormatter.DEFAULT_INSTANCE,
options.getLogLevel());
187
188     MonkeyRunnerStarter runner =
new MonkeyRunnerStarter(options);
189     int error = runner.run();
190
191
192     System.exit(error);
193   }
194 }

代码3-2-1 MonkeyRunnerStart - main

这里主要做了三件事情:

  • 179行去处理用户启动monkeyrunner的时候输入的命令行参数
  • 188行去初始化MonkeyRunnerStarter,里面主要是初始化了ChimpChat,ChimpChat又去开启AndroidDebugBridge进程和开启DeviceMonitor设备监控线程,我们往后小节会进行详细分析
  • 189行去把monkeyrunner运行起来,包括带脚本参数的情况和不待脚本参数直接提供jython命令行的情况

我们这一节会先去分析下monkeyrunner是如何对参数进行处理的,我们跳转到MonkeyRunnerOptions这个类里面的processOptions这个方法:

 93   public static MonkeyRunnerOptions processOptions(String[] args)
 94   {
 95     int index = 0;
 96
 97     String hostname = DEFAULT_MONKEY_SERVER_ADDRESS;
 98     File scriptFile = null;
 99     int port = DEFAULT_MONKEY_PORT;
100     String backend = "adb";
101     Level logLevel = Level.SEVERE;
102
103     ImmutableList.Builder<File> pluginListBuilder = ImmutableList.builder();
104     ImmutableList.Builder<String> argumentBuilder = ImmutableList.builder();
105     while (index < args.length) {
106       String argument = args[(index++)];
107
108       if ("-s".equals(argument)) {
109         if (index == args.length) {
110           printUsage("Missing Server after -s");
111           return null;
112         }
113         hostname = args[(index++)];
114       }
115       else if ("-p".equals(argument))
116       {
117         if (index == args.length) {
118           printUsage("Missing Server port after -p");
119           return null;
120         }
121         port = Integer.parseInt(args[(index++)]);
122       }
123       else if ("-v".equals(argument))
124       {
125         if (index == args.length) {
126           printUsage("Missing Log Level after -v");
127           return null;
128         }
129
130         logLevel = Level.parse(args[(index++)]);
131       } else if ("-be".equals(argument))
132       {
133         if (index == args.length) {
134           printUsage("Missing backend name after -be");
135           return null;
136         }
137         backend = args[(index++)];
138       } else if ("-plugin".equals(argument))
139       {
140         if (index == args.length) {
141           printUsage("Missing plugin path after -plugin");
142           return null;
143         }
144         File plugin = new File(args[(index++)]);
145         if (!plugin.exists()) {
146           printUsage("Plugin file doesn‘t exist");
147           return null;
148         }
149
150         if (!plugin.canRead()) {
151           printUsage("Can‘t read plugin file");
152           return null;
153         }
154
155         pluginListBuilder.add(plugin);
156       } else if (!"-u".equals(argument))
157       {
158         if ((argument.startsWith("-")) && (scriptFile == null))
159         {
160
161
162           printUsage("Unrecognized argument: " + argument + ".");
163           return null;
164         }
165         if (scriptFile == null)
166         {
167
168           scriptFile = new File(argument);
169           if (!scriptFile.exists()) {
170             printUsage("Can‘t open specified script file");
171             return null;
172           }
173           if (!scriptFile.canRead()) {
174             printUsage("Can‘t open specified script file");
175             return null;
176           }
177         } else {
178           argumentBuilder.add(argument);
179         }
180       }
181     }
182
183     return new MonkeyRunnerOptions(hostname,
port,
scriptFile,
backend,
logLevel,
pluginListBuilder.build(),
argumentBuilder.build());
184   }
185 }

代码8-2-2 MonkeyRunnerOptions  - processOptions

这里首先请看99-101行的几个变量初始化,如果用户在命令行中没有指定对应的参数,那么这些默认参数就会被使用,我们且看下这些默认值分别是什么:

  • hostname:对应‘-s‘参数,默认值是‘127.0.0.1‘,也就是本机,将会forward给目标设备运行的monkey,所以加上下面的转发port等同于目标机器在listen的monkey服务
  • port :对应‘-p‘参数,默认值是‘12345‘,也就是monkey默认监听端口
  • backend :对应‘-be‘参数,默认值是‘adb‘,其实往后看代码我们会发现它也只是支持’adb‘而已。这里需要注意的是这是一个隐藏参数,命令行的help没有显示该参数
  • logLevel :对应‘-v‘参数,默认值‘SEVERE‘,也就是说只打印严重的log

代码往下就是对用户输入的参数的解析并保存了,这里要注意几个隐藏的参数:

  • -u :乍一看以为这是一个什么特别的参数,从156-178行可以看到这个参数处理的意义是:当用户输入‘-u‘的时候不会作任何处理,但当用户输入的是由‘-’开始的但又不是monkeyrunner声称支持的那几个参数的时候,就会根据不同的情况给用户报错。所以这段代码的意思其实就是在用户输入了不支持的参数的时候根据不同的情况给用户提示而已
  • -be :backend,如前所述,只支持‘adb‘
  • -plugin :这里需要一个背景知识,在google官网有说明,用户可以通过遵循一定的规范去编写插件来扩展monkeyrunner的功能,比如在monkeydevice里面按下这个动作是需要通过MonkeyDevice.DOWN这个参数来传给press这个方法的,如果你觉得这样子不好,你希望增加个pressDown这样的方法,里面默认就是用MonkeyDevice.DOWN来驱动MonkeyDevice的press方法,而用户只需要给出坐标点就可以了,那么你就可以遵循google描述的规范去编写一个这方面的插件,到时使用的时候就可以通过python方式直接import进来使用了。本书并不会把MonkeyRunner插件进行重点介绍。

在解析出所有的参数之后,processOptions方法最后根据这些参数来初始化MonkeyRunnerOptions类。我们进入到该构造函数看下它究竟做了什么事情:

 38   private MonkeyRunnerOptions(String hostname, int port, File scriptFile, String backend, Level logLevel, Collection<File> plugins, Collection<String> arguments)
 39   {
 40     this.hostname = hostname;
 41     this.port = port;
 42     this.scriptFile = scriptFile;
 43     this.backend = backend;
 44     this.logLevel = logLevel;
 45     this.plugins = plugins;
 46     this.arguments = arguments;
 47   }

代码8-2-3 MonkeyRunnerOptions - 构造函数

所做的事情非常简单,就是把解析出来的所有参数保存到MonkeyRunnerOptions类的实例里面,今后需要的时候就进去拿就好了。

注:更多文章请关注公众号:techgogogo或个人博客http://techgogogo.com。当然,也非常欢迎您直接微信(zhubaitian1)勾搭。本文由天地会珠海分舵原创。转载请自觉,是否投诉维权看心情。

时间: 2024-11-09 06:44:34

第8章2节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-解析处理命令行参数的相关文章

第4章4节《MonkeyRunner源码剖析》ADB协议及服务: ADB命令行客户端使用简介(原创)

天地会珠海分舵注:本来这一系列是准备出一本书的,详情请见早前博文"寻求合作伙伴编写<深入理解 MonkeyRunner>书籍".但因为诸多原因,没有如愿.所以这里把草稿分享出来,所以错误在所难免.有需要的就参考下吧,转发的话还请保留每篇文章结尾的出处等信息. 从前面几个小节我们知道ADB命令行客户端是存在与主机端的一个命令,用户可以使用该命令来发送服务请求到ADB服务器,ADB服务器再判断该服务请求是主机服务请求还是本地服务请求来决定是否应该将请求传送给远程adbd守护进程

第8章2节《MonkeyRunner源代码剖析》MonkeyRunner启动执行过程-解析处理命令行參数

MonkeyRunnerStarter是MonkeyRunner启动时的入口类,由于它里面包括了main方法.它的整个启动过程主要做了以下几件事情: 解析用户启动MonkeyRunner时从命令行传输进来的參数: 由于MonkeyRunner须要依据指定的參数才干做事情,比方输入的一个须要执行的脚本. 假设确实不知道不论什么參数的话它就会进入MonkeyRunner的交互模式,事实上就是Jythong的交互模式,让用户能够边写代码边执行 启动AndroidDebugBridge: 事实上就是启动

老李推荐: 第8章4节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-启动AndroidDebugBridge 1

老李推荐: 第8章4节<MonkeyRunner源码剖析>MonkeyRunner启动运行过程-启动AndroidDebugBridge 上一节我们看到在启动AndroidDebugBridge的过程中会调用其start方法,而该方法会做2个主要的事情: 715行startAdb:开启AndroidDebugBridge 722-723行:初始化android设备监控并启动DeviceMonitor设备监控线程. 其中第一点我们上一小节已经做了详尽分析了,那么我们往下就去分析下第2点. Dev

老李推荐:第6章5节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-事件

老李推荐:第6章5节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-事件 从网络过来的命令字串需要解析翻译出来,有些命令会在翻译好后直接执行然后返回,但有一大部分命令在翻译后需要转换成对应的事件,然后放入到命令队列里面等待执行.Monkey在取出一个事件执行的时候主要是执行其injectEvent方法来注入事件,而注入事件根据是否需要往系统注入事件分为两种: 需要通过系统服务往系统注入事件:如MonkeyKeyEvent事件会通过系统的InputManager往系

老李推荐:第6章3节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令翻译类

老李推荐:第6章3节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令翻译类 每个来自网络的字串命令都需要进行解析执行,只是有些是在解析的过程中直接执行了事,而有些是需要在解析后创建相应的事件类实例并添加到命令队列里面排队执行.负责这部分工作的就是命令翻译类.那么我们往下还是继续在MonkeySourceNetwork这个范畴中MonkeyCommand类是怎么一回事: 图6-3-1 MonkeyCommand族谱 图中间的MonkeyCommand是一个接口,

老李推荐: 第1章1节《MonkeyRunner源码剖析》概述:前言

老李推荐: 第1章1节<MonkeyRunner源码剖析>概述:前言 前言 相信大家做过安卓移动平台UI自动化开发的必然会用过,至少听过MonkeyRunner这个名字.MonkeyRunner是一个针对安卓平台的UI自动化测试框架,这个框架的其中一个但绝不是唯一的优点是支持用当今非常流行和高效的Python语言来进行脚本开发.同时,它相比Instrumentation框架或者基于Instrumentation的自动化测试框架最大的优点之一就是可以跨应用测试. 这本书不会有什么序言或者致谢什么

第8章1节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-运行环境初始化

首先大家应该清楚的一点是,MonkeyRunner的运行是牵涉到主机端和目标设备端的,流程控制逻辑代码是在PC端运行的,但很多命令请求的执行是在目标机器端执行的.这不像其他框架如UiAutomator是所有东西都完全在目标设备端运行的. 这里我们首先应该去看的不是MonkeyRunnerStarter这个类里面的main这个入口函数,因为monkeyrunner其实是个shell脚本,它就在你的sdk/tools下面,这个shell脚本会先初始化一些变量,然后调用最后面也是最关键的一个命令: .

第5章5节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 获取系统服务引用(原创)

天地会珠海分舵注:本来这一系列是准备出一本书的,详情请见早前博文"寻求合作伙伴编写<深入理解 MonkeyRunner>书籍".但因为诸多原因,没有如愿.所以这里把草稿分享出来,所以错误在所难免.有需要的就参考下吧,转发的话还请保留每篇文章结尾的出处等信息. 上一节我们描述了monkey的命令处理入口函数run是如何调用optionProcess方法来解析命令行参数的.启动参数主要时去指导Monkey时怎么运行起来的,但Monkey作为MonkeyRunner框架的一部分,

第5章2节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 启动流程概览(原创)

天地会珠海分舵注:本来这一系列是准备出一本书的,详情请见早前博文"寻求合作伙伴编写<深入理解 MonkeyRunner>书籍".但因为诸多原因,没有如愿.所以这里把草稿分享出来,所以错误在所难免.有需要的就参考下吧,转发的话还请保留每篇文章结尾的出处等信息. 每个应用都会有一个入口方法来供操作系统调用执行,Monkey这个应用的入口方法就是在Monkey.java这个类里面的,也就是说Monkey.java就是整个Monkey应用的入口类. Monkey作为一个命令行应用,