Android Monkey 压力测试完全指南:稳定性测试、命令参数与崩溃分析实战

引言

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
# 检查 Monkey 是否存在
adb shell monkey --help

# 或直接查看版本
adb shell monkey -v 1

基础命令与参数

最简单的 Monkey 测试

1
2
# 对指定包名发送 1000 个随机事件
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
# --pct-touch:触摸事件占比
adb shell monkey -p com.example.myapp --pct-touch 50 -v 1000

# --pct-motion:滑动事件占比
adb shell monkey -p com.example.myapp --pct-motion 30 -v 1000

# --pct-trackball:轨迹球事件占比
adb shell monkey -p com.example.myapp --pct-trackball 10 -v 1000

# --pct-nav:基本导航事件(上下左右键)
adb shell monkey -p com.example.myapp --pct-nav 10 -v 1000

# --pct-majornav:主要导航事件(回退、菜单键)
adb shell monkey -p com.example.myapp --pct-majornav 10 -v 1000

# --pct-syskeys:系统按键事件(Home、音量键等)
adb shell monkey -p com.example.myapp --pct-syskeys 5 -v 1000

# --pct-appswitch:应用切换事件占比
adb shell monkey -p com.example.myapp --pct-appswitch 5 -v 1000

# --pct-anyevent:任意事件占比
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
# --ignore-crashes:忽略崩溃,继续发送事件
adb shell monkey -p com.example.myapp --ignore-crashes -v 10000

# --ignore-timeouts:忽略 ANR,继续发送事件
adb shell monkey -p com.example.myapp --ignore-timeouts -v 10000

# --ignore-security-exceptions:忽略安全异常
adb shell monkey -p com.example.myapp --ignore-security-exceptions -v 10000

# --ignore-native-crashes:忽略 Native 层崩溃
adb shell monkey -p com.example.myapp --ignore-native-crashes -v 10000

# --monitor-native-crashes:监控 Native 层崩溃
adb shell monkey -p com.example.myapp --monitor-native-crashes -v 10000

# --throttle:事件间隔(毫秒),控制测试速度
adb shell monkey -p com.example.myapp --throttle 300 -v 1000

# --randomize-throttle:随机化事件间隔
adb shell monkey -p com.example.myapp --throttle 300 --randomize-throttle -v 1000

# -s:指定随机种子,用于复现问题
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
# 基础测试:10 万个事件,忽略崩溃和超时
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
# monkey_stress_test.sh

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
# 持续测试 8 小时(约 57600 秒)
# 每秒 5 个事件,总事件数约 288000
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

# 提取所有 ANR 信息
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
# 使用相同的随机种子复现崩溃
# 假设日志中显示 seed=1623456789
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
# 生成完整的系统报告(包含 ANR、崩溃等)
adb bugreport bugreport.zip

# 解压后分析 FS/data/anr/ 目录下的 traces.txt

使用 Android Studio 分析

  1. 打开 Android Studio 的 Logcat 窗口
  2. 过滤进程包名:package:mine
  3. 设置日志级别为 Error
  4. 运行 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
# Monkey 按 Home 键或电源键导致应用被切到后台

# 解决方案:降低系统按键比例或完全禁用
adb shell monkey -p com.example.myapp \
--pct-syskeys 0 \
--pct-anyevent 0 \
-v 1000

问题三:应用被系统杀死

1
2
3
4
5
6
7
8
# Android 系统在内存不足时杀死后台应用

# 解决方案:使用 --ignore-crashes 和 --ignore-timeouts
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
# 启用 Native 崩溃监控
adb shell monkey -p com.example.myapp \
--monitor-native-crashes \
-v 10000

# 崩溃后查看 tombstones
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
# 测试同时监控 CPU 和内存

PACKAGE="com.example.myapp"
LOG="performance_monkey.log"

echo "开始测试并监控性能..." > $LOG

# 后台监控 CPU 和内存
(
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=$!

# 运行 Monkey
adb shell monkey -p $PACKAGE \
--ignore-crashes \
--throttle 200 \
-v 50000 > monkey_output.log 2>&1

# 停止监控
kill $MONITOR_PID

echo "测试完成"

总结

Android Monkey 压力测试的核心要点:

  1. 基础用法adb shell monkey -p 包名 -v 事件数 即可快速启动测试
  2. 参数调优:根据应用类型调整事件比例(游戏增加 touch/motion,工具类增加 nav)
  3. 异常处理:使用 --ignore-crashes--ignore-timeouts 确保测试完成
  4. 崩溃复现:记录随机种子 -s,崩溃后使用相同种子复现问题
  5. 日志分析:重点关注 CRASHANRException 关键字,结合 adb logcat 定位根因
  6. 速度控制:使用 --throttle 控制事件发送频率,模拟真实用户操作节奏

Monkey 是发现稳定性问题的利器,但仅适用于 Activity 级别的随机测试。对于复杂的业务流程,还需要配合 Espresso、UI Automator 等自动化测试框架进行定向测试。