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

来源:互联网 发布:女生围巾品牌 知乎 编辑:程序博客网 时间:2024/05/22 16:55

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)勾搭。本文由天地会珠海分舵原创。转载请自觉,是否投诉维权看心情。


1 0
原创粉丝点击