
1. 项目概述为什么我们需要Monkey在移动应用开发与测试的日常工作中我们经常会遇到一个灵魂拷问这个App到底稳不稳这里的“稳”不仅仅指功能正常更指在各种意想不到的用户操作、系统事件冲击下应用能否保持不崩溃、不卡死、不闪退。手动测试覆盖的场景有限而自动化UI测试脚本编写和维护成本又高。这时候一个来自Android官方工具箱的“老将”就派上了大用场——它就是adb shell monkey。Monkey直译为“猴子”其设计理念非常形象让一只猴子在设备上随机乱点、乱滑、乱按以此来模拟用户不可预测的操作对应用进行压力测试和稳定性验证。它不是一个图形化工具而是一个通过ADBAndroid Debug Bridge命令行调用的强大脚本。对于测试工程师、开发者和追求应用质量的任何团队成员来说熟练掌握Monkey就等于拥有了一把快速检验应用健壮性的“压力测试冲锋枪”。它特别适合在发版前进行最后一轮暴力稳定性抽查或者在日常开发中快速复现一些难以捉摸的随机性崩溃。2. Monkey命令核心参数全解与实战策略Monkey命令的基本格式是adb shell monkey [options] event-count其中event-count是指定要发送的随机事件数量。它的强大和灵活完全体现在那一系列[options]里。理解并组合这些参数是高效使用Monkey的关键。2.1 基础控制与包指定参数首先我们必须告诉Monkey这只“猴子”应该在谁的领地里撒野。-p package_name这是最常用的参数用于指定Monkey测试的目标应用包名。你可以同时指定多个包让猴子在几个应用之间跳转测试这对于测试应用间交互或系统整体稳定性很有用。# 测试单个应用例如微信 adb shell monkey -p com.tencent.mm 1000 # 测试多个应用 adb shell monkey -p com.tencent.mm -p com.android.chrome 500注意如果不使用-p参数Monkey将在整个系统范围内随机操作包括系统设置、桌面等这通常不是我们想要的可能会意外修改系统配置。–throttle这个参数用于在事件之间插入固定的延迟毫秒。默认情况下Monkey会以最快的速度疯狂发送事件这虽然压力大但可能无法模拟真实用户操作节奏也容易错过一些需要时间响应的Bug。# 每个事件间隔300毫秒模拟中等速度的用户操作 adb shell monkey -p com.tencent.mm --throttle 300 1000我个人习惯在功能测试时设置–throttle 200-500在纯粹的压力稳定性测试时则去掉这个参数让猴子“疯跑”。2.2 事件类型与比例分配Monkey可以产生多种类型的事件通过参数可以精确控制它们的比例从而让测试场景更具针对性。-v用于指定日志详细程度。-v的个数越多日志越详细。通常使用-v -v -v三个-v来获取最详细的执行信息包括每个被发送的事件、触摸的坐标等这对复现问题至关重要。adb shell monkey -p com.tencent.mm -v -v -v --throttle 100 500–pct-touch调整触摸事件即屏幕上的按下、抬起事件的百分比。这是最主要的操作类型。–pct-motion调整动作事件即屏幕上某处的按下、随机移动、抬起的百分比模拟滑动、拖动。–pct-trackball调整轨迹球事件现在设备很少用了的百分比。–pct-nav调整“基本”导航事件如方向键输入的百分比。–pct-majornav调整“主要”导航事件如回退键、菜单键的百分比。–pct-syskeys调整系统按键事件如Home、音量键的百分比。慎用比例太高容易退出应用。–pct-appswitch调整应用启动Activity跳转事件的百分比。设置一个合适的比例如15%可以很好地测试应用内不同页面的切换。–pct-anyevent调整其他所有事件的百分比。一个模拟重度用户操作的配置示例如下adb shell monkey -p com.example.app \ --pct-touch 40 \ --pct-motion 25 \ --pct-appswitch 20 \ --pct-majornav 10 \ --pct-syskeys 2 \ --pct-anyevent 3 \ --throttle 150 \ -v -v -v 10000这个配置强调触摸和滑动占65%并有20%的概率切换页面同时极少触发系统键适合长时间在应用内进行功能压测。2.3 调试与种子参数-s种子值。这是Monkey一个极其重要的特性。Monkey的“随机”行为是由一个伪随机数生成器产生的-s参数设定了这个生成器的初始种子。只要种子值相同Monkey执行的随机事件序列就完全一致。这用于复现Bug当某次Monkey测试导致崩溃时记录下日志开头显示的种子值下次用相同的种子值即可精确复现操作路径。# 首次运行记录种子值会在日志开头显示如:Monkey: seed12345 ... adb shell monkey -p com.example.app 1000 # 复现时使用相同的种子值 adb shell monkey -p com.example.app -s 12345 1000–dbg-no-events这个参数表示Monkey会启动目标应用并进入一个初始的Activity但不发送任何事件。通常与–setup scriptfile和-v联用用于监控应用在静默状态下的行为如内存泄漏。–kill-process-after-error当Monkey测试过程中目标应用发生错误如ANR、崩溃时Monkey默认会停止并报错但应用进程可能还残留。加上这个参数Monkey会在发生错误后强制杀掉该应用进程。这能保证测试环境的干净便于下一次测试。–ignore-timeouts / –ignore-crashes这两个参数让Monkey在遇到应用无响应ANR或崩溃时不停止测试而是继续发送事件。这用于进行“毁灭性”的耐力测试看看应用在持续异常状态下会怎样。注意使用它们时Monkey的最终结果可能永远是“成功”因为错误被忽略了所以需要结合日志分析。3. 完整实操流程从环境准备到报告分析掌握了核心参数我们来走一遍完整的Monkey测试流程。这个过程不仅仅是执行一条命令更包括前期准备、过程监控和事后分析。3.1 测试环境与前期准备确保ADB环境就绪在电脑上安装Android SDK Platform-Tools并将adb命令添加到系统PATH中。通过adb devices命令确认设备已正确连接。获取目标应用包名如果你不知道包名有几种方法打开应用在电脑命令行输入adb shell dumpsys window | findstr mCurrentFocusWindows或grep mCurrentFocusMac/Linux。输出结果中通常包含包名。使用adb shell pm list packages列出所有包再通过adb shell pm list packages | grep [应用关键词]来过滤查找。确定测试策略想测什么快速冒烟执行1000次快速事件不加延迟检查有无明显崩溃。深度功能遍历执行5000-10000次事件加入合理延迟–throttle 200并提高–pct-appswitch比例让猴子多跳转页面。稳定性压力测试执行50000次甚至更多事件可以忽略超时和崩溃–ignore-timeouts –ignore-crashes让应用在极端条件下长时间运行。清理测试环境测试前最好强制停止目标应用并清除其数据如果需要确保每次测试从一个干净的状态开始。adb shell am force-stop com.example.app adb shell pm clear com.example.app # 警告这会清空应用所有本地数据3.2 命令执行与实时监控准备好后就可以构造命令并执行了。我强烈建议将输出重定向到本地文件方便后续分析。adb shell monkey -p com.example.app \ --pct-touch 40 --pct-motion 25 --pct-appswitch 15 \ --throttle 200 \ -s 20240527 \ -v -v -v 5000 monkey_log_20240527.txt 21这条命令会执行5000个事件并将所有输出包括标准输出和错误输出保存到monkey_log_20240527.txt文件中。执行过程中不要干等着。可以另开一个终端窗口实时监控一些关键指标Logcat日志adb logcat | findstr “FATAL|CRASH|ANR|Exception”过滤关键错误信息。CPU/内存adb shell top -n 1 | findstr com.example.app观察应用资源占用是否异常飙升。当前Activityadb shell dumpsys activity activities | findstr “mResumedActivity”看看猴子把应用带到了哪个页面。3.3 结果分析与问题定位测试完成后无论Monkey命令返回的结果是Monkey finished还是因错误中止真正的功夫都在分析日志文件里。1. 看Monkey执行摘要打开日志文件首先看开头和结尾。开头会显示种子值seed结尾会显示事件执行统计。:Monkey: seed1623456789 count5000 ... // 执行过程中的各种事件日志 ... Events injected: 5000 :Sending rotation degree0, persistfalse :Dropped: keys0 pointers0 trackballs0 flips0 rotations0 ## Network stats: elapsed time1123ms (0ms mobile, 0ms wifi, 1123ms not connected) // Monkey finished如果最后是Monkey finished并且注入的事件数等于你设定的数量说明Monkey按计划执行完毕没有因应用崩溃或ANR而提前停止。2. 搜索崩溃与ANR痕迹在日志中搜索以下关键词CRASH: 应用原生层崩溃C/C代码。Exception: Java层未捕获的异常特别是NullPointerException,IllegalStateException等。ANR in: 应用无响应。Monkey日志里可能不会直接显示ANR对话框但logcat中一定有。Force finishing activity: 系统强制结束某个Activity通常是崩溃的表现。FATAL EXCEPTION: 致命异常。例如在日志中找到这样一段就定位到了一个崩溃// Short Msg: java.lang.NullPointerException // Long Msg: java.lang.NullPointerException: Attempt to invoke virtual method void android.widget.TextView.setText(java.lang.CharSequence) on a null object reference ... // 在日志中找到导致崩溃的种子值和大致事件序列3. 利用种子值复现一旦发现崩溃立刻记下日志开头的种子值例如seed1623456789。然后在完全相同的应用版本和测试环境下重新执行一遍带相同种子值的Monkey命令。理论上你会看到完全相同的操作序列并再次触发崩溃。这时你就可以在开发环境中通过调试器如Android Studio附加到进程在崩溃发生前设置断点或者直接观察崩溃堆栈精准定位问题代码。4. 分析事件流使用三个-v的详细日志你可以看到每个事件的坐标。虽然看起来杂乱但当复现一个与特定UI元素相关的Bug时比如“只有快速点击屏幕左上角某个区域才会崩溃”这些坐标信息就变得极为宝贵。你可以将坐标与UI布局文件进行比对找出是哪个View出了问题。4. 高级技巧、常见问题与避坑指南Monkey用起来简单但想用得好离不开一些实战中积累的经验和技巧。4.1 提升测试效率与针对性的技巧1. 组合使用实现场景化测试不要只用默认设置。根据测试目的组合参数测试横竖屏切换增加–pct-rotation事件的比例注意这个参数在某些设备或系统版本上可能叫–pct-rotation或已失效需实测。测试后台切换适当提高–pct-appswitch比例并配合–throttle模拟用户频繁切换App的场景。测试弱网/断网可以在执行Monkey前通过ADB命令模拟网络状态如adb shell svc wifi disable然后再运行Monkey观察应用在网络异常时的表现。2. 使用脚本进行初始化–setupMonkey支持一个相对少为人知的–setup参数它可以指定一个脚本文件在Monkey发送随机事件之前运行。这个脚本可以是一系列ADB命令用于将应用设置到某个特定状态。例如先登录、进入某个深层页面然后再开始随机测试。这能大大提高测试的深度。# setup_script.mks type raw events count 10 speed 1.0 start data # 以下是通过getevent获取的原始坐标事件用于执行固定操作如点击登录按钮 ...实操心得–setup脚本的编写尤其是原始坐标事件非常繁琐且设备兼容性差在实际工作中我更倾向于先手动或通过自动化脚本将App导航到目标页面然后再开始Monkey测试这样更可控。3. 与性能监控工具结合单纯跑Monkey看是否崩溃是基础。更进阶的做法是在Monkey执行的同时使用adb shell dumpsys meminfo package_name、adb shell procstats或更专业的性能 profiling 工具如Perfetto监控应用的内存、CPU、帧率等指标。这样可以发现那些“不崩溃但越来越卡”的性能劣化问题。4.2 典型问题排查实录问题1Monkey命令执行后立即结束事件计数为0。现象adb shell monkey -p com.xxx 1000瞬间完成日志显示Events injected: 0。排查检查包名是否正确adb shell pm list packages | grep xxx。检查应用是否已安装且启用adb shell pm list packages -e | grep xxx。最常见原因应用没有默认启动的ActivityLauncher Activity。Monkey需要启动一个Activity才能开始发送事件。有些后台服务类应用或插件没有可启动的界面。对于这种应用Monkey无法测试。解决确认你测试的是一个有前台的App。如果是插件需要先启动宿主App。问题2Monkey测试过程中应用跳到了桌面或其他App。现象测试中途被测应用消失了Monkey开始在系统桌面或其他App里操作。排查检查是否误触了系统键。查看详细日志看是否有Sending Key事件键值是KEYCODE_HOME(3) 或KEYCODE_BACK(4)。检查–pct-syskeys比例是否设置过高。通常建议设为0或一个很小的值如1。检查应用自身是否有崩溃或ANR。应用崩溃退出后Monkey会继续向当前顶层Activity可能是桌面发送事件。解决降低–pct-syskeys比例至0或1。使用–kill-process-after-error参数这样应用崩溃后进程被杀死Monkey也会因为找不到目标包而停止避免污染其他应用。问题3无法用种子值复现崩溃。现象第一次Monkey测试导致崩溃记录了种子值。但使用相同种子值再次测试应用却正常运行。排查环境不一致这是最主要的原因。两次测试之间你是否更新了应用版本是否清理了应用数据手机状态网络、电量、其他后台进程是否发生了显著变化Monkey的复现依赖于完全相同的初始状态。竞态条件崩溃是由一个非常精确的时序竞态条件触发的即使操作序列相同系统调度的微小差异也可能导致结果不同。外部依赖崩溃依赖于第一次测试时特定的网络数据、文件内容等第二次测试时这些外部条件变了。解决确保复现环境与初次测试绝对一致相同的APK、相同的数据状态。对于竞态条件导致的崩溃可以尝试在更“干净”的环境下重启手机、关闭其他App多次运行相同种子的测试或者适当增加–throttle延迟有时能提高复现率。问题4Monkey日志过于庞大难以分析。现象执行了10万次事件日志文件高达几百MB用文本编辑器打开都卡更别说找崩溃信息了。解决实时过滤执行命令时可以一边保存完整日志一边用管道过滤关键错误adb shell monkey [options] 100000 21 | tee full_monkey.log | grep -E “CRASH|Exception|ANR|FATAL” error_only.log。事后分析脚本写一个简单的Python或Shell脚本自动扫描日志文件提取包含错误关键词的行及其上下文前后各20行生成一个简短的报告。控制日志级别如果不是为了复现UI操作可以只使用一个-v减少坐标等详细信息的输出。Monkey工具虽然“年事已高”但其在Android应用稳定性测试中的地位依然稳固。它就像一把朴实无华但极其锋利的瑞士军刀不需要复杂的配置和环境一条命令就能对应用发起最直接的冲击。真正用好它的秘诀不在于记住所有参数而在于理解其设计哲学用不可预测性来暴露问题。再结合清晰的测试策略、严谨的日志分析和复现手段你就能让这只“猴子”成为保障应用质量的最得力助手之一。在我经历过的项目中很多隐藏极深、只在特定操作顺序下才会出现的“幽灵Bug”最终都是被Monkey这只不知疲倦的“猴子”给揪出来的。