引言
Android 应用的稳定性直接影响用户体验和应用评分。Monkey 是 Google 提供的一个命令行工具,可以模拟伪随机的用户事件流(点击、滑动、按键、横竖屏切换等),对应用进行压力测试。它能在短时间内发现应用的内存泄漏、ANR(应用无响应)、崩溃等问题。本文将详细介绍 Monkey 的工作原理、常用命令参数、测试策略以及崩溃分析方法。
Monkey 工具概述
什么是 Monkey
Monkey 是 Android SDK 自带的命令行工具,运行在模拟器或真实设备上。它向系统发送伪随机的用户事件序列,通过观察应用的响应来检测稳定性问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| ┌─────────────────────────────────────────────────────────────────────┐ │ Monkey 工作原理 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ Monkey 命令 │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ 伪随机事件 │ 生成随机序列 │ │ │ 生成器 │ • 触摸事件 (Touch) │ │ └──────┬──────┘ • 滑动事件 (Motion) │ │ │ • 按键事件 (Key) │ │ │ • 系统按键 (System Key) │ │ │ • 横竖屏 (Rotation) │ │ ▼ │ │ ┌─────────────┐ │ │ │ 目标应用 │ 接收并处理事件 │ │ │ Activity │ │ │ └──────┬──────┘ │ │ │ │ │ ┌─────┴─────┐ │ │ ▼ ▼ │ │ 正常 异常 │ │ 运行 • 崩溃 (Crash) │ │ • ANR (无响应) │ │ • 内存泄漏 │ │ │ └─────────────────────────────────────────────────────────────────────┘
|
特点与限制
| 特点 |
说明 |
| 随机性强 |
事件序列完全随机,模拟不可预测的用户行为 |
| 速度快 |
每秒可发送数百个事件,快速发现问题 |
| 易用性 |
一行命令即可启动,无需编写测试脚本 |
| 无侵入 |
不需要修改应用代码 |
| 限制 |
说明 |
| 仅测试 Activity |
无法测试 Service、BroadcastReceiver 等组件 |
| 无法验证业务逻辑 |
随机事件不能保证覆盖特定业务流程 |
| 需要人工分析 |
发现崩溃后需要开发者手动定位原因 |
环境准备
连接设备
1 2 3 4 5 6 7
| adb devices
List of devices attached emulator-5554 device ABC123DEF456 device
|
确认 Monkey 可用
1 2 3 4 5
| adb shell monkey --help
adb shell monkey -v 1
|
基础命令与参数
最简单的 Monkey 测试
1 2
| adb shell monkey -p com.example.myapp -v 1000
|
| 参数 |
说明 |
-p |
指定测试包名,可多次使用指定多个包 |
-v |
日志详细程度,可叠加(-v -v -v) |
1000 |
发送的事件总数 |
常用参数详解
事件类型控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| adb shell monkey -p com.example.myapp --pct-touch 50 -v 1000
adb shell monkey -p com.example.myapp --pct-motion 30 -v 1000
adb shell monkey -p com.example.myapp --pct-trackball 10 -v 1000
adb shell monkey -p com.example.myapp --pct-nav 10 -v 1000
adb shell monkey -p com.example.myapp --pct-majornav 10 -v 1000
adb shell monkey -p com.example.myapp --pct-syskeys 5 -v 1000
adb shell monkey -p com.example.myapp --pct-appswitch 5 -v 1000
adb shell monkey -p com.example.myapp --pct-anyevent 5 -v 1000
|
| 事件类型 |
参数 |
说明 |
| 触摸 |
--pct-touch |
屏幕点击 |
| 滑动 |
--pct-motion |
屏幕滑动 |
| 轨迹球 |
--pct-trackball |
轨迹球移动 |
| 基本导航 |
--pct-nav |
方向键 |
| 主要导航 |
--pct-majornav |
回退、菜单 |
| 系统按键 |
--pct-syskeys |
Home、音量等 |
| 应用切换 |
--pct-appswitch |
启动新 Activity |
| 任意事件 |
--pct-anyevent |
其他类型事件 |
调试与约束参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| adb shell monkey -p com.example.myapp --ignore-crashes -v 10000
adb shell monkey -p com.example.myapp --ignore-timeouts -v 10000
adb shell monkey -p com.example.myapp --ignore-security-exceptions -v 10000
adb shell monkey -p com.example.myapp --ignore-native-crashes -v 10000
adb shell monkey -p com.example.myapp --monitor-native-crashes -v 10000
adb shell monkey -p com.example.myapp --throttle 300 -v 1000
adb shell monkey -p com.example.myapp --throttle 300 --randomize-throttle -v 1000
adb shell monkey -p com.example.myapp -s 12345 -v 1000
|
| 参数 |
作用 |
使用场景 |
--ignore-crashes |
崩溃后继续测试 |
需要测试完所有事件 |
--ignore-timeouts |
ANR 后继续测试 |
需要测试完所有事件 |
--ignore-security-exceptions |
忽略权限异常 |
测试未授权操作 |
--throttle |
控制事件发送速度 |
模拟真实用户操作频率 |
-s |
设置随机种子 |
复现崩溃场景 |
实战测试方案
方案一:基础稳定性测试
1 2 3 4 5 6 7 8 9 10 11 12 13
| adb shell monkey -p com.example.myapp \ --ignore-crashes \ --ignore-timeouts \ --ignore-security-exceptions \ --pct-touch 40 \ --pct-motion 30 \ --pct-appswitch 10 \ --pct-majornav 10 \ --pct-syskeys 10 \ --throttle 200 \ -v -v -v \ 100000 > monkey_log.txt 2>&1
|
方案二:深度压力测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #!/bin/bash
PACKAGE="com.example.myapp" EVENT_COUNT=500000 THROTTLE=100 SEED=$(date +%s)
LOG_FILE="monkey_${SEED}.log"
echo "开始 Monkey 压力测试" echo "包名: ${PACKAGE}" echo "事件数: ${EVENT_COUNT}" echo "随机种子: ${SEED}" echo "日志文件: ${LOG_FILE}"
adb shell monkey -p ${PACKAGE} \ --ignore-crashes \ --ignore-timeouts \ --ignore-security-exceptions \ --ignore-native-crashes \ --monitor-native-crashes \ --pct-touch 35 \ --pct-motion 25 \ --pct-trackball 5 \ --pct-nav 5 \ --pct-majornav 10 \ --pct-syskeys 5 \ --pct-appswitch 10 \ --pct-anyevent 5 \ --throttle ${THROTTLE} \ -s ${SEED} \ -v -v -v \ ${EVENT_COUNT} > ${LOG_FILE} 2>&1
echo "测试完成,分析日志..." grep -E "CRASH|ANR|Exception|Error" ${LOG_FILE} | head -20
|
方案三:事件比例优化(模拟游戏场景)
游戏应用需要大量的触摸和滑动操作:
1 2 3 4 5 6 7 8 9 10 11
| adb shell monkey -p com.example.game \ --ignore-crashes \ --ignore-timeouts \ --pct-touch 50 \ --pct-motion 35 \ --pct-syskeys 5 \ --pct-appswitch 5 \ --pct-anyevent 5 \ --throttle 150 \ -v -v -v \ 200000 > game_monkey.log 2>&1
|
方案四:长时间稳定性测试
1 2 3 4 5 6 7 8
|
adb shell monkey -p com.example.myapp \ --ignore-crashes \ --ignore-timeouts \ --throttle 200 \ -v -v -v \ 288000 > long_run_monkey.log 2>&1 &
|
日志分析与崩溃定位
Monkey 日志结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| ┌─────────────────────────────────────────────────────────────────────┐ │ Monkey 日志结构 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ :Monkey: seed=12345 count=1000 │ │ :AllowPackage: com.example.myapp │ │ :IncludeCategory: android.intent.category.LAUNCHER │ │ │ │ // 事件统计 │ │ Event percentages: │ │ 0: 40.0% // touch │ │ 1: 30.0% // motion │ │ ... │ │ │ │ // 发送的事件 │ │ Sending Touch (ACTION_DOWN): 0:(240.0,400.0) │ │ Sending Touch (ACTION_UP): 0:(240.0,400.0) │ │ ... │ │ │ │ // 崩溃信息(关键) │ │ // CRASH: com.example.myapp (pid 12345) │ │ // Short Msg: java.lang.NullPointerException │ │ // Long Msg: Attempt to invoke... on a null object reference │ │ // Build Label: ... │ │ // Stack Trace: │ │ // at com.example.myapp.MainActivity.onClick(MainActivity.java:45)│ │ │ │ // 测试结束统计 │ │ Events injected: 1000 │ │ Dropped: keys=0 pointers=0 trackballs=0... │ │ ## Network stats: elapsed time=5000ms... │ │ │ └─────────────────────────────────────────────────────────────────────┘
|
关键信息提取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| grep -n "CRASH" monkey_log.txt
grep -n "ANR" monkey_log.txt
grep -n -E "Exception|Error" monkey_log.txt
grep "seed=" monkey_log.txt
grep "Events injected" monkey_log.txt
grep -A 20 "CRASH:" monkey_log.txt > crash_details.txt
|
崩溃复现
1 2 3 4 5 6 7
|
adb shell monkey -p com.example.myapp \ -s 1623456789 \ --throttle 200 \ -v -v -v \ 1000
|
结合日志分析工具
实时查看系统日志
1 2 3 4 5 6 7 8
| adb logcat -c
adb logcat | grep -E "AndroidRuntime|ActivityManager|MyApp"
adb logcat *:E
|
导出 Bugreport
1 2 3 4
| adb bugreport bugreport.zip
|
使用 Android Studio 分析
- 打开 Android Studio 的 Logcat 窗口
- 过滤进程包名:
package:mine
- 设置日志级别为 Error
- 运行 Monkey 测试,实时观察崩溃
常见问题与解决方案
问题一:Permission Denial
1 2 3 4 5 6 7
| java.lang.SecurityException: Permission Denial...
adb shell monkey -p com.example.myapp \ --ignore-security-exceptions \ -v 1000
|
问题二:系统按键导致测试中断
1 2 3 4 5 6 7
|
adb shell monkey -p com.example.myapp \ --pct-syskeys 0 \ --pct-anyevent 0 \ -v 1000
|
问题三:应用被系统杀死
1 2 3 4 5 6 7 8
|
adb shell monkey -p com.example.myapp \ --ignore-crashes \ --ignore-timeouts \ --ignore-kill-after-bad-error \ -v 10000
|
问题四:Native 崩溃分析
1 2 3 4 5 6 7 8
| adb shell monkey -p com.example.myapp \ --monitor-native-crashes \ -v 10000
adb shell ls /data/tombstones/ adb pull /data/tombstones/tombstone_00 .
|
高级用法
多包同时测试
1 2 3 4 5 6
| adb shell monkey -p com.example.app1 \ -p com.example.app2 \ -p com.example.app3 \ --throttle 200 \ -v 50000
|
结合性能监控
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #!/bin/bash
PACKAGE="com.example.myapp" LOG="performance_monkey.log"
echo "开始测试并监控性能..." > $LOG
( while true; do adb shell dumpsys meminfo $PACKAGE | grep "TOTAL" >> $LOG adb shell top -n 1 -p $(adb shell pidof $PACKAGE) >> $LOG sleep 5 done ) & MONITOR_PID=$!
adb shell monkey -p $PACKAGE \ --ignore-crashes \ --throttle 200 \ -v 50000 > monkey_output.log 2>&1
kill $MONITOR_PID
echo "测试完成"
|
总结
Android Monkey 压力测试的核心要点:
- 基础用法:
adb shell monkey -p 包名 -v 事件数 即可快速启动测试
- 参数调优:根据应用类型调整事件比例(游戏增加 touch/motion,工具类增加 nav)
- 异常处理:使用
--ignore-crashes 和 --ignore-timeouts 确保测试完成
- 崩溃复现:记录随机种子
-s,崩溃后使用相同种子复现问题
- 日志分析:重点关注
CRASH、ANR、Exception 关键字,结合 adb logcat 定位根因
- 速度控制:使用
--throttle 控制事件发送频率,模拟真实用户操作节奏
Monkey 是发现稳定性问题的利器,但仅适用于 Activity 级别的随机测试。对于复杂的业务流程,还需要配合 Espresso、UI Automator 等自动化测试框架进行定向测试。