Android之Monkey源码分析(第四篇:命令行参数解析过程分析与参数作用阐述)

前言

    在第三篇文章中,分析了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的代码,如何写命令行解析了,完美的分析,下一篇我们将分析下一个步骤

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值