前言
在第三篇文章中,分析了Monkey程序的主线程执行过程,其中Monkey的run()方法包含了大部分的执行流程,今天这篇文章主要分析命令行参数是如何解析到内存中,又是如何在monkey程序中使用这些命令行参数的……Let us go!
run()方法分析
private int run(String[] args) {
………………省略代码…………
mArgs = args;
………………省略代码…………
mNextArg = 0;
………………省略代码…………
if (!processOptions()) {
return -1;
}
………………省略代码…………
}
位于Monkey类中的run()方法是monkey程序主线程执行流程中的一个主要方法,在run()方法中,有命令行参数解析的代码,它对Monkey对象持有的mArgs、以及持有的mNextArg进行初始化工作,接下来调用了一个processOptions()方法完成命令行参数的解析。processOptions()方法执行结束后,它的返回值会被继续使用,如果返回false,说明解析命令行参数时出现了错误,此时整个run()方法会向调用者返回-1。
接下来分析今天的主角,processOptions()方法是如何完成命令行参数解析的
processOptions()方法分析
private boolean processOptions() {
// quick (throwaway) check for unadorned command
if (mArgs.length < 1) {
showUsage();
return false;
}
try {
String opt;
Set<String> validPackages = new HashSet<>();
while ((opt = nextOption()) != null) {
if (opt.equals("-s")) {
mSeed = nextOptionLong("Seed");
} else if (opt.equals("-p")) {
validPackages.add(nextOptionData());
} else if (opt.equals("-c")) {
mMainCategories.add(nextOptionData());
} else if (opt.equals("-v")) {
mVerbose += 1;
} else if (opt.equals("--ignore-crashes")) {
mIgnoreCrashes = true;
} else if (opt.equals("--ignore-timeouts")) {
mIgnoreTimeouts = true;
} else if (opt.equals("--ignore-security-exceptions")) {
mIgnoreSecurityExceptions = true;
} else if (opt.equals("--monitor-native-crashes")) {
mMonitorNativeCrashes = true;
} else if (opt.equals("--ignore-native-crashes")) {
mIgnoreNativeCrashes = true;
} else if (opt.equals("--kill-process-after-error")) {
mKillProcessAfterError = true;
} else if (opt.equals("--hprof")) {
mGenerateHprof = true;
} else if (opt.equals("--match-description")) {
mMatchDescription = nextOptionData();
} else if (opt.equals("--pct-touch")) {
int i = MonkeySourceRandom.FACTOR_TOUCH;
mFactors[i] = -nextOptionLong("touch events percentage");
} else if (opt.equals("--pct-motion")) {
int i = MonkeySourceRandom.FACTOR_MOTION;
mFactors[i] = -nextOptionLong("motion events percentage");
} else if (opt.equals("--pct-trackball")) {
int i = MonkeySourceRandom.FACTOR_TRACKBALL;
mFactors[i] = -nextOptionLong("trackball events percentage");
} else if (opt.equals("--pct-rotation")) {
int i = MonkeySourceRandom.FACTOR_ROTATION;
mFactors[i] = -nextOptionLong("screen rotation events percentage");
} else if (opt.equals("--pct-syskeys")) {
int i = MonkeySourceRandom.FACTOR_SYSOPS;
mFactors[i] = -nextOptionLong("system (key) operations percentage");
} else if (opt.equals("--pct-nav")) {
int i = MonkeySourceRandom.FACTOR_NAV;
mFactors[i] = -nextOptionLong("nav events percentage");
} else if (opt.equals("--pct-majornav")) {
int i = MonkeySourceRandom.FACTOR_MAJORNAV;
mFactors[i] = -nextOptionLong("major nav events percentage");
} else if (opt.equals("--pct-appswitch")) {
int i = MonkeySourceRandom.FACTOR_APPSWITCH;
mFactors[i] = -nextOptionLong("app switch events percentage");
} else if (opt.equals("--pct-flip")) {
int i = MonkeySourceRandom.FACTOR_FLIP;
mFactors[i] = -nextOptionLong("keyboard flip percentage");
} else if (opt.equals("--pct-anyevent")) {
int i = MonkeySourceRandom.FACTOR_ANYTHING;
mFactors[i] = -nextOptionLong("any events percentage");
} else if (opt.equals("--pct-pinchzoom")) {
int i = MonkeySourceRandom.FACTOR_PINCHZOOM;
mFactors[i] = -nextOptionLong("pinch zoom events percentage");
} else if (opt.equals("--pct-permission")) {
int i = MonkeySourceRandom.FACTOR_PERMISSION;
mFactors[i] = -nextOptionLong("runtime permission toggle events percentage");
} else if (opt.equals("--pkg-blacklist-file")) {
mPkgBlacklistFile = nextOptionData();
} else if (opt.equals("--pkg-whitelist-file")) {
mPkgWhitelistFile = nextOptionData();
} else if (opt.equals("--throttle")) {
mThrottle = nextOptionLong("delay (in milliseconds) to wait between events");
} else if (opt.equals("--randomize-throttle")) {
mRandomizeThrottle = true;
} else if (opt.equals("--wait-dbg")) {
// do nothing - it's caught at the very start of run()
} else if (opt.equals("--dbg-no-events")) {
mSendNoEvents = true;
} else if (opt.equals("--port")) {
mServerPort = (int) nextOptionLong("Server port to listen on for commands");
} else if (opt.equals("--setup")) {
mSetupFileName = nextOptionData();
} else if (opt.equals("-f")) {
mScriptFileNames.add(nextOptionData());
} else if (opt.equals("--profile-wait")) {
mProfileWaitTime = nextOptionLong("Profile delay" +
" (in milliseconds) to wait between user action");
} else if (opt.equals("--device-sleep-time")) {
mDeviceSleepTime = nextOptionLong("Device sleep time" +
"(in milliseconds)");
} else if (opt.equals("--randomize-script")) {
mRandomizeScript = true;
} else if (opt.equals("--script-log")) {
mScriptLog = true;
} else if (opt.equals("--bugreport")) {
mRequestBugreport = true;
} else if (opt.equals("--periodic-bugreport")){
mGetPeriodicBugreport = true;
mBugreportFrequency = nextOptionLong("Number of iterations");
} else if (opt.equals("--permission-target-system")){
mPermissionTargetSystem = true;
} else if (opt.equals("-h")) {
showUsage();
return false;
} else {
Logger.err.println("** Error: Unknown option: " + opt);
showUsage();
return false;
}
}
MonkeyUtils.getPackageFilter().addValidPackages(validPackages);
} catch (RuntimeException ex) {
Logger.err.println("** Error: " + ex.toString());
showUsage();
return false;
}
// If a server port hasn't been specified, we need to specify
// a count
if (mServerPort == -1) {
String countStr = nextArg();
if (countStr == null) {
Logger.err.println("** Error: Count not specified");
showUsage();
return false;
}
try {
mCount = Integer.parseInt(countStr);
} catch (NumberFormatException e) {
Logger.err.println("** Error: Count is not a number: \"" + countStr + "\"");
showUsage();
return false;
}
}
return true;
}
位于Monkey类中,processOptions()方法用于解析命令行参数
我们使用adb shell monkey -p com.xx.xx 500的时候,作为命令行参数的-p、com.xx.xx、500会成为数组对象中的一个一个的元素,Monkey对象持有的mArags指向的是这个数组对象,数组对象保存的就是前文提到的每个命令行参数
说明:根据java标准,命令行参数会根据空白字符隔开,同时传递到静态方法main()中,args作为数组对象,保存着每个命令行参数作为元素,所以-p com.xx.xx 500是作为monkey程序的命令行参数,保存在数组args中哦
1、首先检查命令行参数的数量
// quick (throwaway) check for unadorned command
if (mArgs.length < 1) {
showUsage();
return false;
}
如果命令行参数数量小于1,直接调用showUsage()方法,然后返回false,表示解析命令行参数出错,而showUsage()方法会在标准输出(屏幕)中展示提示信息,最终在控制台提示用户,那些信息就是我们在使用monkey --help的时候,monkey程序的提示信息,后面会再分析showUsage()方法,这里不再表述
2、进入一个大范围的try catch代码块,在try...catch中获取到的运行时异常都会在标准错误中输出,接下来先分析try代码块中的代码,然后再分析catch代码块的代码,这里作者的本意是程序不结束,但是异常信息仍然保留下来,线程堆栈输出到标准输出中
3、创建局部变量
String opt;
创建局部变量opt,用于存储传入的一个命令行字符串参数
4、创建一个HashSet对象
Set<String> validPackages = new HashSet<>();
创建局部变量validPackages,指向一个创建的HashSet对象,这个集合保存的每个元素的类型是字符串,用于存储有效的包名(从命令行中传入的包名),使用集合的目的是为了去重
5、进入while循环
while ((opt = nextOption()) != null) { //省略 }
首先调用nextOption()方法,并且将值存储在局部变量opt中,只要通过nextOption()方法可以获取到一个命令行参数,该while循环会一直继续,直到最后没有可用的命令行参数,nextOption()方法会返回null,此时while循环会终止,接下来是解析一个一个解析命令行参数重要逻辑
6、while循环内部,对取出来的命令行参数先进行分类与保存
-s、-p、-v、等等,不同的命令行参数,执行不同的逻辑
主要是解析-xxx后面的参数,使用的方法是
nextOptionLong()用于将-xxx后面的命令行参数由字符串解析为long类型
nextOptionData()用于将-xxx后面的参数解析为String类型
这段代码包含monkey程序支持的所有命令行参数,它们最终都会保存在各自不同的Monkey对象持有的实例变量中,同时类型也会从字符串转换为对应的对象,供程序中使用
举几个例子
1、-s参数
if (opt.equals("-s")) { mSeed = nextOptionLong("Seed");
-s 20 此时命令行字符串20会保存在Monkey对象持有的实例变量mSeed中,并且已经转换为long类型了
2、-p参数
} else if (opt.equals("-p")) { validPackages.add(nextOptionData());
-p com.xx.xx 包名com.xx.xx会保存在创建的HashSet对象validPackages中(注意:set用于保存保存多个包名,且不会重名,相当于对包名做了去重,这个很赞,所以我们传递重复包名是没事的)
3、-c参数
} else if (opt.equals("-c")) { mMainCategories.add(nextOptionData());
-c xxxx 此时指定的xxxx会保存在Monkey对象持有的mMainCategories,mMainCategories是一个ArrayList对象,nextOptionData()是会返回一个字符串的,这里作者的思路很清晰
4、-v参数
} else if (opt.equals("-v")) { mVerbose += 1;
-v 每当我们设置了一个-v参数,则Monkey对象持有的整形实例变量mVerbose会+1,值越大表示调试等级越大,标准输出的日志就越多,这个设计非常赞,动态log参数,而不是固定的打印一堆log,比我们之前写的程序不知道好了多少倍,根据需要输出必要的日志
5、--ignore-crashes参数
} else if (opt.equals("--ignore-crashes")) { mIgnoreCrashes = true;
--ignore-crashes 当我们设置了这个命令行参数,Monkey对象持有的mIgnoreCrashes标志位会值为true,表示有任何App异常,monkey程序不会停止,一直保持运行,此时由于不需要获取下一个参数,所以并没有调用nextOptionData()等等方法,这个参数我们经常用
6、--ignore-timeouts
} else if (opt.equals("--ignore-timeouts")) { mIgnoreTimeouts = true;
--ignore-timeouts 当我们传入了这个命令行参数,Monkey对象持有的mIgnoreTimeouts会赋值为true,表示出现ANR时,monkey程序继续运行,不停止
7、--ignore-security-exceptions
} else if (opt.equals("--ignore-security-exceptions")) { mIgnoreSecurityExceptions = true;
--ignore-security-exceptions 当我们传入这个命令行参数,Monkey对象持有的mIgnoreSecurityExceptions会赋值为true,表示出现系统级别的安全异常,monkey程序也继续执行,不停止
8、--monitor-native-crashes
} else if (opt.equals("--monitor-native-crashes")) { mMonitorNativeCrashes = true;
--monitor-native-crashes 当我们在命令行参数中指定了这个参数,Monkey对象持有的mMonitorNativeCrashes会被赋值为true,表示是否监控native层崩溃,当c或c++程序出现崩溃,monkey程序会调用bugreport程序,收集一些崩溃日志,新版本的Android其实调用不调用都行!
9、--ignore-native-crashes
} else if (opt.equals("--ignore-native-crashes")) { mIgnoreNativeCrashes = true;
--ignore-native-crashes 当我们在命令行参数中指定这个参数,Monkey对象持有的mIgnoreNativeCrashes会赋值为true,表示出现native层崩溃时,monkey程序继续执行,不停止运行
10、--kill-process-after-error
} else if (opt.equals("--kill-process-after-error")) { mKillProcessAfterError = true;
--kill-process-after-error 当设置了这个参数,Monkey对象持有的mKillProcessAfterError会赋值为true,表示出现ANR错误后、系统无响应错误时,是否要求AMS系统服务杀死掉目标进程
11、--hprof
} else if (opt.equals("--hprof")) { mGenerateHprof = true;
--hprof:Monkey对象持有的mGenerateHprof会赋值为true,表示需要构建进程的堆内存信息
12、--match-description
} else if (opt.equals("--match-description")) { mMatchDescription = nextOptionData();
--match-description xxx Monkey对象持有的mMatchDescription将持有字符串xxx,这个命令行参数,会限制app崩溃、anr、系统崩溃发生时,必须是mMatchDescription中指定的内容出现后,才会去调用bugreport收集错误信息
13、--pct-touch
} else if (opt.equals("--pct-touch")) { int i = MonkeySourceRandom.FACTOR_TOUCH; mFactors[i] = -nextOptionLong("touch events percentage");
--pct-touch xxx Monkey对象持有的数组对象mFactors中的FACTOR_TOUCH下标处会存储xxx的反方向值,比如xxx是40,实际会存储的是-40,这是有意而为的,mFactors数组对象负责保存每一个事件的比例,此事件表示触摸事件,至于为啥用负数,卖个关子
14、--pct-motion xxx
} else if (opt.equals("--pct-motion")) { int i = MonkeySourceRandom.FACTOR_MOTION; mFactors[i] = -nextOptionLong("motion events percentage");
--pct-motion xxx:…………同样在下标FACTOR_MOTION处保存事件比例值,最终保存的值也是一个负数值,此事件也表示手势事件
15、--pct-trackball
} else if (opt.equals("--pct-trackball")) { int i = MonkeySourceRandom.FACTOR_TRACKBALL; mFactors[i] = -nextOptionLong("trackball events percentage");
--pct-trackball:…………在下标FACTOR_TRACKBALL处保存的事件比例值,表示轨迹球事件
16、--pct-rotation
} else if (opt.equals("--pct-rotation")) { int i = MonkeySourceRandom.FACTOR_ROTATION; mFactors[i] = -nextOptionLong("screen rotation events percentage");
--pct-rotation:…………在下标FACTOR_ROTATION保存的事件比例值,表示屏幕翻转事件的出现比例值
17、--pct-syskeys
} else if (opt.equals("--pct-syskeys")) { int i = MonkeySourceRandom.FACTOR_SYSOPS; mFactors[i] = -nextOptionLong("system (key) operations percentage");
--pct-syskeys:…………在下标FACTOR_SYSOPS中保存的事件比例值,表示系统事件,比如menu、back、home等事件
18、--pct-nav
} else if (opt.equals("--pct-majornav")) { int i = MonkeySourceRandom.FACTOR_MAJORNAV; mFactors[i] = -nextOptionLong("major nav events percentage");
--pct-nav:…………在下标FACTOR_NAV中保存,表示实体按键(过去那种十字导航键)的事件比例值,现在几乎不再使用这样的事件
19、--pct-majornav
} else if (opt.equals("--pct-majornav")) { int i = MonkeySourceRandom.FACTOR_MAJORNAV; mFactors[i] = -nextOptionLong("major nav events percentage");
--pct-majornav:………在下标FACTOR_MAJORNAV中保存
20、--pct-appswitch
} else if (opt.equals("--pct-appswitch")) { int i = MonkeySourceRandom.FACTOR_APPSWITCH; mFactors[i] = -nextOptionLong("app switch events percentage");
--pct-appswitch:…………在下标FACTOR_APPSWITCH中保存,表示切换不同App的比例
21、--pct-flip
} else if (opt.equals("--pct-flip")) { int i = MonkeySourceRandom.FACTOR_FLIP; mFactors[i] = -nextOptionLong("keyboard flip percentage");
--pct-flip:…………在下标FACTOR_FLIP中保存,表示键盘翻转的比例
-pct-anyevent:…………在下标FACTOR_ANYTHING中保存,表示任意事件的比例
--pct-pinchzoom:…………在下标FACTOR_PINCHZOOM中保存,表示多指的事件比例
--pct-permission:…………在下标FACTOR_PERMISSION中保存,表示权限事件的比例
--pkg-blacklist-file:…………在Monkey对象持有的mPkgBlacklistFile保存,指定的黑名单文件名
--pkg-whitelist-file:…………在Monkey对象持有的mPkgWhitelistFile保存,指定的白名单文件名(内容为可以启动的包名)
--throttle:………………在Monkey对象持有的mThrottle保存着,表示事件触发的间隔时间
--randomize-throttle:…………在Monkey对象持有的mRandomizeThrottle中保存着结果,表示是否开启事件触发的间隔时间的随机性,比如设定的间隔时间为300ms,则随机时间每次为300ms或者以下
--wait-dbg:…………这个命令行参数,什么也不干,作者留着测试用的
--dbg-no-events:…………在Monkey对象持有的mSendNoEvents会赋值为true
--port:…………在Monkey对象持有的mServerPort会持有命令行值,并且会强制转为int,这个port,会让monkey程序作为Server端,监听着指定的端口(使用Unit Domain Socket执行进程间通信)
--setup:…………在Monkey对象持有的mSetupFileName保存一个文件名,当Monkey从脚本文件中执行时,首先会读取这个文件
-f:…………在Monkey对象持有的mScriptFileNames(一个List),因为可以指定多个脚本文件名
--profile-wait:…………在Monkey对象持有的mProfileWaitTime保存改值,当monkey使用脚本文件作为事件源时,可以使用这个实例变量保存的值,作为两个事件之间的延迟时间
--device-sleep-time:…………在Monkey对象持有的mDeviceSleepTime,保存该值,同样是在monkey执行脚本文件作为事件源时,使用的一个用于设备休息时间的参数
--randomize-script:…………在Monkey对象持有的mRandomizeScript保存该标志位,表示是否使用随机脚本文件(任意脚本中选择一个)
--script-log:……在Monkey对象持有的mScriptLog负责保存,表示monkey程序执行脚本事件来源时,是否开启日志
--bugreport:……在Monkey对象持有的mRequestBugreport负责保存, 表示被测App出现崩溃后,是否执行bugreport命令(可执行文件)
--periodic-bugreport:……比如--periodic-bugreport 10,在Monkey对象持有的mGetPeriodicBugreport负责保存一个标志位,设置了,即mGetPeriodicBugreport为true,同时设置的值,赋值给mBugreportFrequency保存!
--permission-target-system:………Monkey对象持有的mPermissionTargetSystem为true
-h:…………当解析出-h时,直接调用showUsage()、还会直接return一个false,直接结束方法
else 处理没有匹配成功的--xx参数,这时候会将错误信息写入到标准错误中,展示的是
** Error: Unknown option: 你传的参数,然后返回false,表示解析命令行参数出错
7、将存储的允许启动App的集合对象,保存到MonkeyUtils类持有的PackageFilter对象持有的mValidPackages集合中,此时PackageFilter对象开始持有所有允许启动App的情况
MonkeyUtils.getPackageFilter().addValidPackages(validPackages);
8、catch代码块的处理
发生任何的RuntimeException,包括子类,只会在标准错误中,将异常信息打印出来,然后调用showUsage()方法,并直接false,说明解析命令行失败
9、处理并解析事件总数
当没有设置mServerPort,即没有指定过命令行参数--port,先执行nextArg()方法,获取表示事件执行总数的字符串(说明:运行monkey时,命令行参数的最后一个参数表示事件总数),然后判断保存到的局部变量countStr是否为null,如果为null,说明用户没有设置事件总数,此时我们可以看到常见的错误信息:
** Error: Count not specified,然后还有调用showUsage()方法,告诉你Monkey的使用方法,返回false,表示解析命令行失败
如果获取到countStr,则将字符串类型解析成int类型,并保存到Monkey对象持有的mCount中,如果解析的时候无法转换为整型,会捕获NumberFormatException,并提示
Logger.err.println("** Error: Count is not a number: \"" + countStr + "\""); showUsage(); return false;
接着调用showUsage(),最后返回false,表示解析命令行失败
10、向调用者返回true
这时候表示解析命令行成功
分析完processOptions()方法,接下来再分析一下在processOptions()方法中调用过的方法,它们是
1、nextOption()
2、nextArg()
3、nextOptionLong()
4、nextOptionData()
5、showUsage()方法
捎带分析一个MonkeyUtils类(简单说一下)
nextOption()方法分析
private String nextOption() {
if (mNextArg >= mArgs.length) {
return null;
}
String arg = mArgs[mNextArg];
if (!arg.startsWith("-")) {
return null;
}
mNextArg++;
if (arg.equals("--")) {
return null;
}
if (arg.length() > 1 && arg.charAt(1) != '-') {
if (arg.length() > 2) {
mCurArgData = arg.substring(2);
return arg.substring(0, 2);
} else {
mCurArgData = null;
return arg;
}
}
mCurArgData = null;
Logger.err.println("arg=\"" + arg + "\" mCurArgData=\"" + mCurArgData + "\" mNextArg="
+ mNextArg + " argwas=\"" + mArgs[mNextArg-1] + "\"" + " nextarg=\"" +
mArgs[mNextArg] + "\"");
return arg;
}
用于从命令行参数中,提取一个参数,比如-p com.android.xx,则会提取出-p,最后返回提取出的参数
1、检查下标值是否超出范围
Monkey对象持有的mNextArg用于记录当前正在解析的那个参数的下标,它的下标值从0开始,如果它的值大于等于数组对象(保存着每个参数的数组对象)的长度,说明已经遍历完所有的参数,此时会返回null,表示已经没有参数供解析了
2、提取一个参数保存到局部变量中
从mArgs数组对象中,提取一个参数,然后保存到局部变量arg中,比如 -p com.android.xx,此时会提取出-p保存到arg中
3、检查当前参数是否以-开头
如果不是-开头的参数,直接返回,是-则继续
4、将下标增加1位
更新mNextArg指向下一个的参数
5、检查当前参数是否等于--
如果是--,则也返回null
6、有条件的解析参数
当参数大于1、且第2个值不是减号-的时候,又对两种情况做出处理
第一种是参数大于2个字符的情况:
另一参数是小于等于2个字符的情况:
7、mCurArgData赋值为null
8、向标准错误输出一行日志
9、处理-x这样的参数,返回给调用者参数(注意这里在标准错误中输出的参数的调试情况)
nextArg()方法分析
private String nextArg() {
if (mNextArg >= mArgs.length) {
return null;
}
String arg = mArgs[mNextArg];
mNextArg++;
return arg;
}
位于Monkey类中,用于返回下一个参数,不过类型为String,这个方法只在解析monkey事件数的时候使用了,我也是醉了
nextOptionLong()方法分析
private long nextOptionLong(final String opt) {
long result;
try {
result = Long.parseLong(nextOptionData());
} catch (NumberFormatException e) {
Logger.err.println("** Error: " + opt + " is not a number");
throw e;
}
return result;
}
位于Monkey类中,用于返回下一个参数,好处是直接转换为了long类型,当在解析long类型出现异常时,直接捕获,然后先打印日志,再抛给调用者处理
nextOptionData()方法分析
private String nextOptionData() {
if (mCurArgData != null) {
return mCurArgData;
}
if (mNextArg >= mArgs.length) {
return null;
}
String data = mArgs[mNextArg];
Logger.err.println("data=\"" + data + "\"");
mNextArg++;
return data;
}
位于Monkey类中,用于返回下一个字符串参数,并且将当前指向的参数下标+1,主要就是干这个了
showUsage()方法分析
private void showUsage() {
StringBuffer usage = new StringBuffer();
usage.append("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]\n");
usage.append(" [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]\n");
usage.append(" [--ignore-crashes] [--ignore-timeouts]\n");
usage.append(" [--ignore-security-exceptions]\n");
usage.append(" [--monitor-native-crashes] [--ignore-native-crashes]\n");
usage.append(" [--kill-process-after-error] [--hprof]\n");
usage.append(" [--match-description TEXT]\n");
usage.append(" [--pct-touch PERCENT] [--pct-motion PERCENT]\n");
usage.append(" [--pct-trackball PERCENT] [--pct-syskeys PERCENT]\n");
usage.append(" [--pct-nav PERCENT] [--pct-majornav PERCENT]\n");
usage.append(" [--pct-appswitch PERCENT] [--pct-flip PERCENT]\n");
usage.append(" [--pct-anyevent PERCENT] [--pct-pinchzoom PERCENT]\n");
usage.append(" [--pct-permission PERCENT]\n");
usage.append(" [--pkg-blacklist-file PACKAGE_BLACKLIST_FILE]\n");
usage.append(" [--pkg-whitelist-file PACKAGE_WHITELIST_FILE]\n");
usage.append(" [--wait-dbg] [--dbg-no-events]\n");
usage.append(" [--setup scriptfile] [-f scriptfile [-f scriptfile] ...]\n");
usage.append(" [--port port]\n");
usage.append(" [-s SEED] [-v [-v] ...]\n");
usage.append(" [--throttle MILLISEC] [--randomize-throttle]\n");
usage.append(" [--profile-wait MILLISEC]\n");
usage.append(" [--device-sleep-time MILLISEC]\n");
usage.append(" [--randomize-script]\n");
usage.append(" [--script-log]\n");
usage.append(" [--bugreport]\n");
usage.append(" [--periodic-bugreport]\n");
usage.append(" [--permission-target-system]\n");
usage.append(" COUNT\n");
Logger.err.println(usage.toString());
}
位于Monkey类中,主要是向标准错误流中输出,Monkey怎么使用,这也是我们使用Monkey程序出错时,输出的内容……
总结
下次你写Java命令行程序时,就可以参照Monkey的代码,如何写命令行解析了,完美的分析,下一篇我们将分析下一个步骤