AndroidAndroid&iOS被遗忘的软件测试

第5章4节《MonkeyRunner源码剖析》Monkey原理分

2015-03-08  本文已影响157人  天地会珠海分舵

天地会珠海分舵注:本来这一系列是准备出一本书的,详情请见早前博文“寻求合作伙伴编写《深入理解 MonkeyRunner》书籍“。但因为诸多原因,没有如愿。所以这里把草稿分享出来,所以错误在所难免。有需要的就参考下吧,转发的话还请保留每篇文章结尾的出处等信息。

设置好Monkey的CLASSPATH环境变量以指定”/system/framework /framework/monkey.jar“后,/system/bin/monkey这个shell脚本就会通过app_process命令指定monkey的入口类” “com.android.commands.monkey.Monkey”找到main函数开始运行。在运行之前会有一些初始化的工作需要做,其中很重要的一项就是去把用户提供的命令行选项以及参数值给解析出来。

这里只是以”monkey -port 12345”作为一个情景分析了monkey是如何进行命令行参数解析的,至于其他的参数解析原理是相同的,这里就不一一解析了,以下贴出monkey帮助命令打印出来的帮助文档,大家可以看下monkey都支持哪些命令行参数选项:

图5-4-1 monkey支持命令选项


图5-4-1 monkey支持命令选项图5-4-1 monkey支持命令选项

其中 “--port”选项就是去指定monkey服务需要监听的端口,这样客户端就可以连接上来跟monkey服务进行通信了。

下面我们开始对Monkey应用的源代码进行分析,首先这里我们先进入到Monkey类的main函数:

代码5-4-1 Monkey - Main

 414     /**
 415      * Command-line entry point.
 416      *
 417      * @param args The command-line arguments
 418      */
 419     public static void main(String[] args) {
 420         // Set the process name showing in "ps" or "top"
 421         Process.setArgV0("com.android.commands.monkey");
 422         int resultCode = (new Monkey()).run(args);
 423         System.exit(resultCode);
 424     }

重点代码422行首先会去创建一个Monkey类的实例,然后调用run成员方法。这个成员方法比较长,做了很多事情,往下会慢慢为你一一道来。其中做的事情之一就是在开始时调用processOptions方法来解析命令行参数。

代码5-4-2 Monkey - run 初始化命令

 425     /**
 426      * Run the command!
 427      *
 428      * @param args The command-line arguments
 429      * @return Returns a posix-style result code. 0 for no error.
 430      */
 431   private int run(String[] args)
 432   {
...//省略其他一些成员变量的初始化
 444     this.mArgs = args;
 445     this.mNextArg = 0;
...//省略其他一些成员变量的初始化

 450     if (!processOptions()) {
 451       return -1;
 452     }
...//其他关键方法将在今后小节进行分析,这里先略过
}

代码第450行之前做的都是一些成员变量初始化的动作,初始化后就会调用450行的processOptions进行真正的命令行参数解析并设置真正的来自命令行的成员变量。注意第444行会把来自命令行的参数数组赋给Monkey实例的mArgs成员变量保存起来,然后在445行把mNextArg这个游标设置成0,这个变量是用来遍历mArgs这个命令行参数数组的。

代码5-4-3 Monkey - processOptions

 632     /**
 633      * Process the command-line options
 634      *
 635      * @return Returns true if options were parsed with no apparent errors.
 636      */
 637     private boolean processOptions() {
            ...
 644             String opt; 
 645            while ((opt = nextOption()) != null)
 646                    if (opt.equals("-s")) {
 647                        this.mSeed = nextOptionLong("Seed");
 648            }
              ...//省略类似处理代码
 713                else if (opt.equals("--port")) {
 714                        this.mServerPort = ((int)nextOptionLong("Server\
                             port to listen on for commands"));
 715            }
                 ...//省略类似处理代码
 742              }
            ...
765     return true;
766   }

代码从645行开始到742行会执行一个while循环,每循环一次都会调用nextOption方法取一个命令行参数选项进来进行解析,与之对应的是取该命令行的参数值的方法nextOptionXXX等,其中XXX根据该参数值是长整型还是字串类型而不同:

例如713-714行就是去处理MonkeyRunner启动monkey的命令”monkey -port 12345”传过来的参数”-port 12345”的关键,它判断如果参数是”--port”的话,就会去取得紧跟着这个参数选项的参数值,也就是12345,然后把它保存到Monkey的成员变量mServerPort里面以供今后使用。注意这里的nextOptionLong方法的参数“Server port to listen on for commands”其实只是调试用途而已。

最后我们看下nextOption是如何从mArgs数组中根据当前mNextArg的数组游标来来获得下一个参数选项,以及与其对应的nextOptionLong是如何取得对应的下一个参数选贤的参数值的。我们先看nextOption:

代码5-4-4 Monkey - nextOption

1117     private String nextOption() {
1118         if (mNextArg >= mArgs.length) {
1119             return null;
1120         }
1121         String arg = mArgs[mNextArg];
1122         if (!arg.startsWith("-")) {
1123             return null;
1124         }
1125         mNextArg++;
1126         if (arg.equals("--")) {
1127             return null;
1128         }
1129         if (arg.length() > 1 && arg.charAt(1) != '-') {
1130             if (arg.length() > 2) {
1131                 mCurArgData = arg.substring(2);
1132                 return arg.substring(0, 2);
1133             } else {
1134                 mCurArgData = null;
1135                 return arg;
1136             }
1137         }
1138         mCurArgData = null;
1139         return arg;
1140     }

mArgs成员变量是一个String类型的数组,从本节上面的分析可以知道命令行传进来的参数列表是被保存在里面的。这段代码比较短也比较简单。分析之前我们先对monkey支持的命令行格式进行一些描述,这样大家就很容易看明白这些代码的意思了:

知道了monkey支持的命令行格式之后,还是有必要对nextOption方法用到的几个Monkey类的成员变量做一些解析:

以下是这个方法的分析,大意就是从mArgs命令行参数数组中取得一个参数,然后经过一系列的判断是否合法,最后如果合法的话就直接返回。分析过程中大家需要注意的是,整个方法做的事情是取得参数选项,而不是取得参数值,所以不要被参数值ARGS给影响你的理解,该ARGS是由其他nextOptionXXX方法来获得的,比如下一个将要分析的去获得Long类型的参数值的方法nextOptionLong,而不是在这里。

还是以命令”monkey --port 12345”这个命令为例子。在通过nextOption方法获得对应的参数选项如”--port”之后,跟着需要做的就是去获得对应的“12345”这个参数选项值了。这里12345被认为是一个长整型的数值,所以调用的是nextOptionLong这个方法。

代码5-4-5 Monkey - nextOptionLong

1157     /**
1158      * Returns a long converted from the next data argument, with error handling
1159      * if not available.
1160      *
1161      * @param opt The name of the option.
1162      * @return Returns a long converted from the argument.
1163      */
1164     private long nextOptionLong(final String opt) {
1165         long result;
1166         try {
1167             result = Long.parseLong(nextOptionData());
1168         } catch (NumberFormatException e) {
1169             System.err.println("** Error: " + opt + " is not a number");
1170             throw e;
1171         }
1172         return result;
1173     }

重点在1167行,通过nextOptionData的调用获得字串类型的命令行参数值,然后通过Long的parseLong方法来将该字串转成长整型的目标值。

不知道大家在前面nextOption分析中有没有注意到一点,凡是参数选项和参数值合并在一起的情况(比如-zARGS),该方法都会在获得需要的参数选项(比如-z)的同时,把参数值(ARGS)也一并分析出来并保存在本Monkey类的成员变量mCurArgData成员变量里面;而凡是参数选项和参数值分开的情况(比如-z ARGS),该nextOption方法都只是把参数选项给分析出来,而把mCurArgData设置成null。其实下面我们要分析的nextOptionData就是要根据这两种情况来获得对应的参数选项的参数值的。

代码5-4-6 Monkey - nextOptionData

1141     /**
1142      * Return the next data associated with the current option.
1143      *
1144      * @return Returns the data string, or null of there are no more arguments.
1145      */
1146     private String nextOptionData() {
1147         if (mCurArgData != null) {
1148             return mCurArgData;
1149         }
1150         if (mNextArg >= mArgs.length) {
1151             return null;
1152         }
1153         String data = mArgs[mNextArg];
1154         mNextArg++;
1155         return data;
1156     }

该方法所做的事情主要就是如前所说的根据mCurArgData这个用来保存参数值的成员变量是否为null(也就是说在之前的nextOption获取参数选项的方法中是否已经顺便把参数值一并获取了)而分不同的方法来获得参数选项对应的参数值:

——— 未完待续———


作者:天地会珠海分舵
微信公众号:TechGoGoGo
微博:http://weibo.com/techgogogo
CSDN:http://blog.csdn.net/zhubaitian

上一篇下一篇

猜你喜欢

热点阅读