Android测试之Monkey原理及源码分析(二)

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 "" HUP
exec 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<ComponentName> 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<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;
        }

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<String> validPackages = new HashSet<>();
        //检查黑名单是不是空的
        if ((mPkgWhitelistFile != null)
                && (!loadPackageListFromFile(mPkgWhitelistFile, validPackages))) {
            return false;
        }
        MonkeyUtils.getPackageFilter().addValidPackages(validPackages);
        Set<String> invalidPackages = new HashSet<>();
        if ((mPkgBlacklistFile != null)
                && (!loadPackageListFromFile(mPkgBlacklistFile, invalidPackages))) {
            return false;
        }
        MonkeyUtils.getPackageFilter().addInvalidPackages(invalidPackages);
        return true;
    }

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

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值