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

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

  • 解析用户启动MonkeyRunner时从命令行传输进来的參数: 由于MonkeyRunner须要依据指定的參数才干做事情,比方输入的一个须要执行的脚本。

    假设确实不知道不论什么參数的话它就会进入MonkeyRunner的交互模式,事实上就是Jythong的交互模式,让用户能够边写代码边执行
  • 启动AndroidDebugBridge: 事实上就是启动ADBserver,由于MonkeyRunner跟设备通信的一个很重要的方法之中的一个就是通过向ADBserver发送命令来请求目标设备的服务
  • 启动设备监控线程: 事实上这个是在启动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 - 构造函数

所做的事情很easy。就是把解析出来的全部參数保存到MonkeyRunnerOptions类的实例里面。今后须要的时候就进去拿就好了。

注:很多其它文章请关注公众号:techgogogo或个人博客http://techgogogo.com。当然。也很欢迎您直接微信(zhubaitian1)勾搭。

本文由天地会珠海分舵原创。转载请自觉,是否投诉维权看心情。

时间: 2024-08-02 02:49:43

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

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

MonkeyRunnerStarter是MonkeyRunner启动时的入口类,因为它里面包含了main方法.它的整个启动过程主要做了以下几件事情: 解析用户启动MonkeyRunner时从命令行传输进来的参数: 因为MonkeyRunner需要根据指定的参数才能做事情,比如输入的一个需要执行的脚本.如果确实不知道任何参数的话它就会进入MonkeyRunner的交互模式,其实就是Jythong的交互模式,让用户可以边写代码边执行 启动AndroidDebugBridge: 其实就是启动ADB服务

MonkeyRunner源代码分析之启动

在工作中由于要追求完毕目标的效率,所以很多其它是强调实战.注重招式.关注怎么去用各种框架来实现目的.可是假设一味仅仅是注重招式.缺少对原理这个内功的了解,相信自己非常难对各种框架有更深入的理解. 从几个月前開始接触ios和android的自己主动化測试.原来是本着只为了提高測试团队工作效率的心态先行作浅尝即止式的研究,然后交给測试团队去边实现边自己研究.最后由于各种原因果然是浅尝然后就止步了,而自己终于也离开了上一家公司. 换了工作这段时间抛开全部杂念和曾经的困扰专心去学习研究各个框架的使用,逐

第6章1节《MonkeyRunner源代码剖析》Monkey原理分析-事件源-事件源概览

在上一章中我们有简要的介绍了事件源是怎么一回事.可是并没有进行详细的描写叙述.那么往下的这几个小节我们就须要把这方面的知识给补充完整. 这一节我们先主要环绕MonkeySourceNetwork这个事件源来学习事件源的框架结构.首先,要理解事件源,必须先搞清楚几个问题: 事件从哪里来? Monkey的事件来源有多个方面,可是作为MonkeyRunner框架的一部分,它的事件来源主要是来自MonkeyRunner通过网络Socket(USB/TCP协议)发送过来的命令字串.MonkeySource

第14章4节《MonkeyRunner源代码剖析》 HierarchyViewer实现原理-装备ViewServer-port转发

在初始化HierarchyViewer的实例过程中,HierarchyViewer会调用自己的成员方法setupViewServer来把ViewServer装备好,那么我们这里先看下这种方法: 39 private void setupViewServer() { 40 DeviceBridge.setupDeviceForward(mDevice); 41 if (!DeviceBridge.isViewServerRunning(mDevice)) { 42 if (!DeviceBridg

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

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

第14章5节《MonkeyRunner源代码剖析》 HierarchyViewer实现原理-装备ViewServer-查询ViewServer执行状态

上一小节我们描写叙述了HierarchyViewer是怎样组建ADB协议命令来实现ViewServer的port转发的.在port转发设置好后,下一个要做的事情就是去检測目标设备端ViewServer线程是否已经启动起来了.我们进入setupViewServer调用的DeviceBridge的isViewServerRunning方法: 165 public static boolean isViewServerRunning(IDevice device) { 166 final boolea

《Entity Framework 6 Recipes》中文翻译系列 (40) ------ 第七章 使用对象服务之从跟踪器中获取实体与从命令行生成模型(想解决EF第一次查询慢的,请阅读)

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 7-5  从跟踪器中获取实体 问题 你想创建一个扩展方法,从跟踪器中获取实体,用于数据保存前执行一些操作. 解决方案 假设你有如图7-7所示的模型. 图7-7. 包含实体Technician和ServiceCall的模型 在这个模型中,每个技术员(technician)都有一些业务服务请求(service call),业务服务请求包含联系人姓名,问题.使用代码清单7-4,创建一个扩展方法获取

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

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

x264源代码简单分析:x264命令行工具(x264.exe)

本文简单分析x264项目中的命令行工具(x264.exe)的源代码.该命令行工具可以调用libx264将YUV格式像素数据编码为H.264码流. 函数调用关系图 X264命令行工具的源代码在x264中的位置如下图所示. 单击查看更清晰的图片 X264命令行工具的源代码的调用关系如下图所示. 单击查看更清晰的图片 从图中可以看出,X264命令行工具调用了libx264的几个API完成了H.264编码工作.使用libx264的API进行编码可以参考<最简单的视频编码器:基于libx264(编码YUV