Monkey源码分析

monkey源码地址

Monkey启动的主流程

  1. 在main函数中调用了run方法
  2. 处理命令行选项(把外部传入的参数进行初始化处理)
  3. 加载包黑名单或白名单(如果指定)
  4. 如果没有从外部传-s参数,随机因子默认为0,就会在此处初始化,获取当前的时间戳为随机因子数
  5. 检查所有系统接口是否都可用(检查ActivityManager,IWindowManager,IPackageManager)
  6. 创建随机数生成器(注:传入的随机因子数一样的时候,每次的随机数出现的顺序也就是一样的,所以这里其实是一个伪随机数生成器)
  7. 根据不同的参数去初始化不同的事件源(MonkeySourceRandom类(随机生成事件)、MonkeySourceScript(从脚本获取事件)、MonkeySourceNetwork(从网络获取事件))
  8. 设置已设置的事件比例(替换系统默认的事件比例值)
  9. 验证事件源发生器(验证用户传递的事件数比例是否符合要求)
  10. 循环执行monkey事件(当没有出现系统报错或者当前执行事件数没有大于设置的事件数时一直会执行)
  11. 从步骤7初始化的mEventSource对象中取出事件
  12. 对事件进行注入执行
// Monkey的源码
        public class Monkey {
            //程序入口
            public static void main(String[] args) {
                Process.setArgV0("com.android.commands.monkey");
                Logger.err.println("args: " + Arrays.toString(args));
                // 1.调用run方法
                int resultCode = (new Monkey()).run(args);
                System.exit(resultCode);
            }
                        
         	private int run(String[] args) {
                ...
                 // 2.处理命令行选项
                if (!processOptions()) {
                return -1;
                }
                // 3.加载包黑名单或白名单(如果指定)。
                if (!loadPackageLists()) {
                return -1;
                }
                // 4.如果没有从外部传-s参数,随机因子默认为0,就会在此处初始化
                if (mSeed == 0) {
                mSeed = System.currentTimeMillis() + System.identityHashCode(this);
                    }
                // 5.检查所有系统接口是否都可用(检查ActivityManager,IWindowManager,IPackageManager)
                if (!getSystemInterfaces()) {
                return -3;
                }
                // 6.创建随机数生成器
                mRandom = new Random(mSeed);
        
                // 7.根据不同的参数去初始化不同的事件源
                if (mScriptFileNames != null && mScriptFileNames.size() == 1) {
                    // script mode, ignore other options
                    mEventSource = new MonkeySourceScript(mRandom, mScriptFileNames.get(0), mThrottle,
                    mRandomizeThrottle, mProfileWaitTime, mDeviceSleepTime);
                    mEventSource.setVerbose(mVerbose);
                    
                    mCountEvents = false;
                } else if (mScriptFileNames != null && mScriptFileNames.size() > 1) {
                            ...
                mEventSource = new MonkeySourceRandomScript(mScriptFileNames,
                mThrottle, mRandomizeThrottle, mRandom,
                mProfileWaitTime, mDeviceSleepTime, mRandomizeScript);
                   
                } else if (mServerPort != -1) {try {
                            ...
                   mEventSource = new MonkeySourceNetwork(mServerPort);
                
                } else {
                    // 默认随机源
                    if (mVerbose >= 2) { // check seeding performance
                    Logger.out.println("// Seeded: " + mSeed);
                    }
                    mEventSource = new MonkeySourceRandom(mRandom, mMainApps,
                    mThrottle, mRandomizeThrottle, mPermissionTargetSystem);
                    mEventSource.setVerbose(mVerbose);
                    // 8.设置已设置的比例因子
                    for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
                        if (mFactors[i] <= 0.0f) {
                            ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
                            }
                        }
                
                    // 在随机模式下,从activity开始
                    ((MonkeySourceRandom) mEventSource).generateActivity();
                }
                // 9.验证源发生器
                if (!mEventSource.validate()) {
                    return -5;
                }
                        ...
                        
                    mNetworkMonitor.start();
                    // 10.循环执行monkey事件
                    int crashedAtCycle = runMonkeyCycles();
                    mNetworkMonitor.stop();
                    }
                }

             //循环monkey事件
             private int runMonkeyCycles() {
                ...
                // 循环执行monkey事件(当没有出现系统报错或者当前执行事件数没有大于设置的事件数时)
                while (!systemCrashed && cycleCounter < mCount) {
                    11.从步骤7初始化的mEventSource对象中取出事件
                    MonkeyEvent ev = mEventSource.getNextEvent();
                    if (ev != null) {
                        12.对事件进行注入执行
                        int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
                        ...
                    }
                ...
            }    
        }

验证事件源发生器(MonkeySourceRandom)

主要验证用户设置的各种事件数比例符不符合要求,然后再通过用户设置的事件数比例和系统的事件数比例进行调整。

  1. 验证源发生器
  2. 算出用户设置的事件比例总值和剩余比例值和剩余的事件数
  3. 如果用户请求是> 100%,直接返回false报出错误
  4. 如果用户指定了所有事件的权重,那么它们需要是100%,如果不是直接返回false报出错误
  5. 计算必要的调整(把剩余的比例进行均分)
  6. 通过调整默认值或将用户值翻转回>0来修复所有值(把均分的值重新计算分布到各个事件上)
  7. 最后,规范化并转换为运行求和(把所有的事件分布在0-100%的区间值中)
        public class Monkey {
            private int run(String[] args) {
                    // 1.验证源发生器
                    if (!mEventSource.validate()) {
                        return -5;
                    }
                    // 设置已设置的比例因子(有传参过来的就设置,没有传参的默认为0)
                    for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
                        if (mFactors[i] <= 0.0f) {
                            ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
                        }
                    }
            }
        }
    
        public class MonkeySourceRandom implements MonkeyEventSource {
            public boolean validate() {
                boolean ret = true;
                ...
                //调用adjustEventFactors方法
                   return ret & adjustEventFactors();
            }    
        
 
            //调整百分比(在应用用户值之后)
            private boolean adjustEventFactors() {
                // go through all values and compute totals for user & default values
                // 检查所有值并计算用户和默认值的总数
                float userSum = 0.0f;
                float defaultSum = 0.0f;
                int defaultCount = 0;
                for (int i = 0; i < FACTORZ_COUNT; ++i) {
                    // 2.用户设置为零或负值
                    if (mFactors[i] <= 0.0f) {   
                        // 这个地方为什么是减等于,因为初始化的时候设置的是负数值(Monkey类的processOptions()方法中初始化的时候可以看到)
                        userSum -= mFactors[i];
                    } else {
                        defaultSum += mFactors[i];
                        ++defaultCount;
                    }
                }
        
                
                // 3.如果用户请求是> 100%,直接返回false报出错误
                if (userSum > 100.0f) {
                    Logger.err.println("** Event weights > 100%");
                    return false;
                }
        
                
                // 4.如果用户指定了所有的权重,那么它们需要是100%
                if (defaultCount == 0 && (userSum < 99.9f || userSum > 100.1f)) {
                    Logger.err.println("** Event weights != 100%");
                    return false;
                }
        
                // 5.计算必要的调整
                float defaultsTarget = (100.0f - userSum);
                float defaultsAdjustment = defaultsTarget / defaultSum;
        
         
                // 6.通过调整默认值或将用户值翻转回>0来修复所有值
                for (int i = 0; i < FACTORZ_COUNT; ++i) {
                    if (mFactors[i] <= 0.0f) {   // user values are zero or negative
                        mFactors[i] = -mFactors[i];
                    } else {
                        mFactors[i] *= defaultsAdjustment;
                    }
                }
        
                if (mVerbose > 0) {
                    Logger.out.println("// Event percentages:");
                    for (int i = 0; i < FACTORZ_COUNT; ++i) {
                        Logger.out.println("//   " + i + ": " + mFactors[i] + "%");
                    }
                }
        
                // 7.最后,规范化并转换为运行求和
                float sum = 0.0f;
                for (int i = 0; i < FACTORZ_COUNT; ++i) {
                    sum += mFactors[i] / 100.0f;
                    mFactors[i] = sum;
                }
                return true;
            }
    }

Monkey事件的生成

  1. 从mEventSource对象中取出事件,进行注入执行
  2. 如果队列为空,则首先生成事件
  3. 生成随机事件(根据随机数生成器生成的随机数 匹配在此区间内的事件)
  4. 把生成的事情添加到MonkeyEventQueue对象尾部
  5. 获取队列最前面的事件,移除出队列,返回给Monkey进行操作。
        public class Monkey {
                //循环monkey事件
                private int runMonkeyCycles() {
                        ...
                        // 循环执行monkey事件(当没有出现系统报错或者当前执行事件数没有大于设置的事件数时)
                        while (!systemCrashed && cycleCounter < mCount) {
                            1.从mEventSource对象中取出事件
                            MonkeyEvent ev = mEventSource.getNextEvent();
                            if (ev != null) {
                                对事件进行注入执行
                                int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
                            ...
                        }
                    ...
                }    
            

        public class MonkeySourceRandom implements MonkeyEventSource {

                public MonkeyEvent getNextEvent() {
                    // 2.如果队列为空,则首先生成事件
                    if (mQ.isEmpty()) {
                        generateEvents();
                    }
                    mEventCount++;
                    // 5.获取最前面的值,移除出队列
                    MonkeyEvent e = mQ.getFirst();
                    mQ.removeFirst();
                    return e;
                }
                //3.生成随机事件
                private void generateEvents() {
                    float cls = mRandom.nextFloat();
                    int lastKey = 0;
                    // 根据随机数生成器生成的随机数 匹配在此区间内的事件
                    if (cls < mFactors[FACTOR_TOUCH]) {
                        generatePointerEvent(mRandom, GESTURE_TAP);
                        return;
                    } else if (cls < mFactors[FACTOR_MOTION]) {
                        generatePointerEvent(mRandom, GESTURE_DRAG);
                        return;
                    } else if (cls < mFactors[FACTOR_PINCHZOOM]) {
                        generatePointerEvent(mRandom, GESTURE_PINCH_OR_ZOOM);
                        return;
                    } else if (cls < mFactors[FACTOR_TRACKBALL]) {
                        generateTrackballEvent(mRandom);
                        return;
                    } else if (cls < mFactors[FACTOR_ROTATION]) {
                        generateRotationEvent(mRandom);
                        return;
                    } else if (cls < mFactors[FACTOR_PERMISSION]) {
                        mQ.add(mPermissionUtil.generateRandomPermissionEvent(mRandom));
                        return;
                    }
                    ...
                    //4.把生成的事情添加到MonkeyEventQueue对象中
                    mQ.addLast(e);
                }
            }
        }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Monkey测试是一种随机化的测试方法,通常用于测试应用程序的稳定性和兼容性。它会在应用程序中随机生成触摸、滑动、按键等事件,以模拟用户的操作。如果应用程序不能正确处理这些事件,就会导致程序崩溃或出现其他异常情况。 下面是一般的monkey测试报错流程: 1. 生成随机事件:Monkey测试工具会生成随机的触摸、滑动、按键等事件,以模拟用户的操作。 2. 执行随机事件:应用程序会接收到这些随机事件,并进行相应处理。 3. 检查应用程序的响应:Monkey测试工具会检查应用程序的响应,包括界面响应、功能是否正常等。 4. 记录错误:如果应用程序出现了异常情况,Monkey测试工具会记录错误信息并输出日志。 5. 分析错误:测试人员会根据错误信息和日志,分析问题的原因。 6. 修复错误:开发人员根据测试人员提供的错误信息,修复应用程序中的问题。 7. 再次测试:修复后的应用程序会再次进行Monkey测试,以确认问题是否已经解决。 根据上述流程,如果应用程序不能正确地处理随机事件,就会导致程序崩溃或出现其他异常情况。在这种情况下,Monkey测试工具会记录错误信息并输出日志,测试人员根据错误信息和日志,分析问题的原因,开发人员再根据测试人员提供的错误信息,修复应用程序中的问题。最后,修复后的应用程序会再次进行Monkey测试,以确认问题是否已经解决。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值