monkey源码分析

monkey源码位置:development/cmds/monkey/cmds/monkey/src/com/android/commands/monkey/
adb shell monkey时是执行了位于/system/bin/下的monkey脚本:

# Script to start "monkey" on the device, which has a very rudimentary# shell.#base=/system   #将base设为system路径export CLASSPATH=$base/framework/monkey.jar    #将monkey.jar路径设置为拉萨市path环境变量trap "" HUPexec app_process $base/bin com.android.commands.monkey.Monkey $*   #通过app_process启动monkey

monkey.java

一.从monkey.java入手,源代码:
https://github.com/aosp-mirror/platform_development/blob/master/cmds/monkey/src/com/android/commands/monkey/Monkey.java
二.monkey命令入口
monkey.java::main()如下:

    public static void main(String[] args) {        // Set the process name showing in "ps" or "top"        Process.setArgV0("com.android.commands.monkey");        Logger.err.println("args: " + Arrays.toString(args));        int resultCode = (new Monkey()).run(args);        System.exit(resultCode);    }    ……

1.将进程名"com.android.commands.monkey"加入进程显示列表,以便通过ps或者top查看
2.log打印
3.通过(new Monkey()).run()运行monkey,处理参数
三.run()
monkey.java::run()第一部分源码——传参处理:

    /**     * Run the command!     *     * @param args The command-line arguments     * @return Returns a posix-style result code. 0 for no error.     */    private int run(String[] args) {        // 如果参数带有“--wait-debug”进入debug状态        for (String s : args) {            if ("--wait-dbg".equals(s)) {                Debug.waitForDebugger();            }        }        // 为部分命令行参数设置默认值        mVerbose = 0;        mCount = 1000;        mSeed = 0;        mThrottle = 0;        // 将参数字符串赋值给mArgs        mArgs = args;        for (String a: args) {            Logger.err.println(" arg: \"" + a + "\"");        }        mNextArg = 0;        // 对mFactors[]数组赋初值,这些元素将用于存储“--pct-xxxx”参数        for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {            mFactors[i] = 1.0f;        }

1.很容易就发现run(args)是用来处理输入参数的
2.为了了解FACTORZ_COUNT这个参数,我们先去monkeySourceRandom.java看一下相关源码:

   public static final int FACTORZ_COUNT       = 12;    // should be last+1

FACTORZ_COUNT应该为最后一个值加1,那么前面都是些什么值呢?前面定义了12个事件,即为monkey参数“–pct-xxxx”触发的相应事件,且这些参数都是final常量,不能修改其值(0一定是FACTOR_TOUCH),源码如下:

    public static final int FACTOR_TOUCH        = 0;    public static final int FACTOR_MOTION       = 1;    public static final int FACTOR_PINCHZOOM    = 2;    public static final int FACTOR_TRACKBALL    = 3;    public static final int FACTOR_ROTATION     = 4;    public static final int FACTOR_PERMISSION   = 5;    public static final int FACTOR_NAV          = 6;    public static final int FACTOR_MAJORNAV     = 7;    public static final int FACTOR_SYSOPS       = 8;    public static final int FACTOR_APPSWITCH    = 9;    public static final int FACTOR_FLIP         = 10;    public static final int FACTOR_ANYTHING     = 11;        …… public MonkeySourceRandom(Random random, List MainApps,            long throttle, boolean randomizeThrottle, boolean permissionTargetSystem) {        // 各事件初始百分占比        mFactors[FACTOR_TOUCH] = 15.0f;        mFactors[FACTOR_MOTION] = 10.0f;        mFactors[FACTOR_TRACKBALL] = 15.0f;        mFactors[FACTOR_ROTATION] = 0.0f;        mFactors[FACTOR_NAV] = 25.0f;        mFactors[FACTOR_MAJORNAV] = 15.0f;        mFactors[FACTOR_SYSOPS] = 2.0f;        mFactors[FACTOR_APPSWITCH] = 2.0f;        mFactors[FACTOR_FLIP] = 1.0f;        // disbale permission by default        mFactors[FACTOR_PERMISSION] = 0.0f;        mFactors[FACTOR_ANYTHING] = 13.0f;        mFactors[FACTOR_PINCHZOOM] = 2.0f;

上一篇有提到说各个事件有初始占比,在构造函数中的源码如上所示,同时monkey可以通过设置参数改变事件的占用百分比,但是总百分比不能超过100%。如果保持默认比例,运行结果应该如下图:

3.monkeySourceRandom.java实现了monkeyEventSource中提供的接口public class MonkeySourceRandom implements MonkeyEventSource,monkeyEventSource接口源码如下:

public interface MonkeyEventSource {    public MonkeyEvent getNextEvent();  //返回下一个monkey event    public void setVerbose(int verbose);   //设置log等级    public boolean validate();  //验证是否有效}

monkey.java::run()第二部分源码——参数分析:

       if (!processOptions()) {            return -1;        }        if (!loadPackageLists()) {            return -1;        }

4.processOptions()是一个重要的方法,用于解析命令行传入的参数,然后将传入的参数结合默认的参数进行校验和匹配:

    private boolean processOptions() {        //如果参数长度小于1,则显示help usage        if (mArgs.length < 1) {            showUsage();            return false;        }        try {            String opt;            Set 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;        }

1)nextOption()

    private String nextOption() {        if (mNextArg >= mArgs.length) {  //如果参数序号大于等于存储参数的数组的总长度(数组是从0开始的,0~length-1)            return null;        }        String arg = mArgs[mNextArg];  //否则就获取序号为mNextArg的参数        if (!arg.startsWith("-")) {    //如果参数不是以“-”开头,返回null            return null;        }        mNextArg++;        if (arg.equals("--")) {  //如果参数只有“--”,返回null            return null;        }        if (arg.length() > 1 && arg.charAt(1) != '-') {  //如果参数长度大于1,且只有一个“-”:-z 或者 -z args            if (arg.length() > 2) {          //如果参数长度大于2,从第3位开始是参数后面跟的args并赋值给mCurArgData,返回“-参数”                mCurArgData = arg.substring(2);                return arg.substring(0, 2);            } else {    //否则该参数后面没有args,直接返回                mCurArgData = null;                return arg;            }        }        mCurArgData = null;        Logger.err.println("arg=\"" + arg + "\" mCurArgData=\"" + mCurArgData + "\" mNextArg="                + mNextArg + " argwas=\"" + mArgs[mNextArg-1] + "\"" + " nextarg=\"" +                mArgs[mNextArg] + "\"");        return arg;    }

如果参数有“–”或者不含数值args直接返回该参数,如果只有“-”且含数值args只返回该参数的前2位,然后将args交给nextOptionLong()处理。
2)nextOptionLong()

   private long nextOptionLong(final String opt) {       long result;       try {           result = Long.parseLong(nextOptionData());   //还是调用nextOptionData()       } catch (NumberFormatException e) {           Logger.err.println("** Error: " + opt + " is not a number");           throw e;       }       return result;   }

args的处理又再交给nextOptionData()去完成。

会调用nextOptionLong()的参数:

  • “-s”:随机数seed,赋值给mSeed
  • “–throttle”:输入延迟,赋值给mThrottle
  • “–port”:tcp端口,赋值给mServerPort
  • “–device-sleep-time”:设备空闲时间,赋值给mDevicesSleepTime
  • “–periodic-bugreport”:bugreport频率,赋值给mBugreportFrequency
  • “–pct-touch”:对应monkeySourceRandom.FACTOR_TOUCH,赋值给mFactors[i]
  • “–pct-motion”:对应monkeySourceRandom.FACTOR_MOTION,赋值给mFactors[i]
  • “–pct-trackball”:对应monkeySourceRandom.FACTOR_TRACKBALL,赋值给mFactors[i]
  • “–pct-rotation”:对应monkeySourceRandom.FACTOR_ROTATION,赋值给mFactors[i]
  • “–pct-syskeys”:对应monkeySourceRandom.FACTOR_SYSOPS,赋值给mFactors[i]
  • “–pct-nav”:对应monkeySourceRandom.FACTOR_NAV,赋值给mFactors[i]
  • “–pct-majornav”:对应monkeySourceRandom.FACTOR_MAJORNAV,赋值给mFactors[i]
  • “–pct-appswitch”:对应monkeySourceRandom.FACTOR_APPSWITCH,赋值给mFactors[i]
  • “–pct-flip”:对应monkeySourceRandom.FACTOR_FLIP,赋值给mFactors[i]
  • “–pct-anyevent”:对应monkeySourceRandom.FACTOR_ANYTHING,赋值给mFactors[i]
  • “–pct-pinchzoom”:对应monkeySourceRandom.FACTOR_PINCHZOOM,赋值给mFactors[i]
  • “–pct-permission”:对应monkeySourceRandom.FACTOR_PERMISSION,赋值给mFactors[i]

3)nextOptionData()

    private String nextOptionData() {  //返回参数,如果参数有带args也返回        if (mCurArgData != null) {               return mCurArgData;        }        if (mNextArg >= mArgs.length) {            return null;        }        String data = mArgs[mNextArg];        Logger.err.println("data=\"" + data + "\"");        mNextArg++;        return data;    }

会调用nextOptionData()的参数:

  • “-p”:添加允许的包名,添加到mValidPackages
  • “-c”:添加允许的类名,添加到mmainCategories
  • “–pkg-blacklist-file”:列入黑名单的包,添加到mPkgBlacklistfile
  • “–pkg-whitelist-file”:列入白名单的包,添加到mPkgWhitelistfile
  • “–setup”:setup脚本名,赋值给mSetupfileName
  • “-f”:monkey脚本名,赋值给mScriptfileNames

4)会返回true的参数:

  • “–ignore-crashes”:运行时忽略所有崩溃,赋值给mIgnoreCrashes
  • “–ignore-timeout”:运行时忽略相应超时,赋值给mIgnoreTimeouts
  • “–ignore-security-exceptions”:运行时忽略安全异常,赋值给mIgnoreSecurityExceptions
  • “–monitor-native-crashes”:运行时若出现native异常则监控,且将错误报告存储在/data/tombstones/下,赋值给mMonitorNativeCrashes
  • “–ignore-native-crashes”:运行时忽略所有系统级native异常,赋值给mIgnoreNativeCrashes
  • “–kill-process-after-error”:运行时出现error则杀掉进程,赋值给mKillProcessAfterError
  • “–hprof”:生成hprof报告,赋值给mGenerateHprof
  • “–randomize-throttle”:插入随机输入延迟,赋值给mRandomizeThrottle
  • “–dbg-no-events”:不发送任何时间只作为长延时观察用户操作,赋值给mSendNoEvents
  • “–randomize-script”:随机运行monkey脚本,赋值给mRandomizeScript
  • “–script-log”:记录monkey脚本日志
  • “–bugreport”:运行崩溃时捕获bugreport,赋值给mRequestBugreport
  • “–periodic-bugreport”:根据bugreport频率来捕获bugreport,赋值给mGetPeriodicBugreport
  • “–permission-target-system”:赋值给mPermissionTargetSystem

5)比较特殊的几个参数:

  • “-v”:增加log日志级别,每写一个-v,mVerbose+=1(初始值为0)
  • “-h”/错误参数:显示help usage
  • “–wait-dbg”:什么都不做,在run()方法一开始就会判断是否有这个参数是否要进入debug模式

5.loadPackageLists()

//导入package列表,白名单和黑名单    private boolean loadPackageLists() {    //检查白名单是不是空的        if (((mPkgWhitelistFile != null) || (MonkeyUtils.getPackageFilter().hasValidPackages()))                && (mPkgBlacklistFile != null)) {            Logger.err.println("** Error: you can not specify a package blacklist "                    + "together with a whitelist or individual packages (via -p).");            return false;        }        Set validPackages = new HashSet<>();        //检查黑名单是不是空的        if ((mPkgWhitelistFile != null)                && (!loadPackageListFromFile(mPkgWhitelistFile, validPackages))) {            return false;        }        MonkeyUtils.getPackageFilter().addValidPackages(validPackages);        Set invalidPackages = new HashSet<>();        if ((mPkgBlacklistFile != null)                && (!loadPackageListFromFile(mPkgBlacklistFile, invalidPackages))) {            return false;        }        MonkeyUtils.getPackageFilter().addInvalidPackages(invalidPackages);        return true;    }

这部分我没有太深究,应该就是加载名单为true之后才能进行之后的操作,否则会有相应的错误提示之类的。。。

更多相关文章

  1. mybatisplus的坑 insert标签insert into select无参数问题的解决
  2. 箭头函数的基础使用
  3. Python技巧匿名函数、回调函数和高阶函数
  4. python list.sort()根据多个关键字排序的方法实现
  5. Android(安卓)多版本多渠道打包
  6. Ubuntu12.04 安装ADB调试环境
  7. Android中关于ComponentName的使用
  8. attrs.xml的使用
  9. Android(安卓)学习笔记 Contacts (一)ContentResolver query 参数

随机推荐

  1. Android的进程与线程模型
  2. Android 性能优化典范(六)
  3. SpannableStringBuilder的使用
  4. 蓝牙协议栈调试记录
  5. android各种组件的监听器
  6. Android -- 跨应用绑定service(AIDL)
  7. Lottie for Android
  8. Android学习心得(七)——SQLite
  9. Android Camera实现方式
  10. Android之系统启动机理