news 2026/10/15 7:01:37

Monkey离线脚本实战:Android稳定性测试自动化方案解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Monkey离线脚本实战:Android稳定性测试自动化方案解析

monkey 离线脚本这个标题,放在多数人的聊天框里可能第一反应是某个安卓工具的偷懒玩法,但真正做稳定性测试的人看到应该会心一笑。这里说的其实就是一套可以脱离人工盯屏、在没有交互式操作的情况下,自动完成压力测试注入、运行监控、日志收集和结果归类的完整流程。我最早接触 monkey 是在几年前刚上手 Android 测试的时候,那时候跑个压力测试就是直接敲一行adb shell monkey 50000,然后盯着终端滚屏,滚完数一下有没有崩溃,再手动去设备里拉日志。这种玩法放到版本回归、发布前的稳定性验证、甚至 CI 流水线里完全没法用,人不可能陪着设备跑三五个小时,更不可能同时盯着几十台机器。真正把这件事做好,靠的就是把 monkey 跑法和日志策略封装成一套离线脚本,让设备脱离人工以后自己完成一切。

这篇文章就按照我从需求拆解、命令研究、脚本架构、实操落地到踩坑记录这条线,把一套完整的 monkey 离线脚本怎么设计、怎么写、怎么排查问题都展开聊聊。适合刚入行的测试新人、被重复回归折磨的自动化测试同学,以及想在持续集成体系里加入稳定性测试能力的团队参考。

1. 项目解读:离线跑 monkey 到底在解决什么问题

1.1 为什么非要把 monkey 变成“离线”的

monkey 是 Android 系统自带的一个命令行工具,全名叫 UI/Application Exerciser Monkey,作用是向设备发送大量的伪随机事件流,比如点击、滑动、旋转、缩放、系统按键等,用来验证应用长时间运行会不会崩溃、会不会无响应。这个工具本身的用法非常简单,但真正投入到实际项目中,你会发现几个绕不开的痛点。

第一个痛点是时间成本。常规回归测试可能要求跑 5 万、10 万甚至 20 万条随机事件,按一般设备的速度跑下来,至少需要两三个小时到一晚上。你不可能一直在工位上盯着屏幕,更不可能在半夜爬起来做日志归档。只要把执行和日志收集做成了自动离线脚本,下班前挂上任务,第二天早上直接看报告就行,设备在跑,人在睡觉,互不干扰。

第二个痛点是日志的有效性。手动跑 monkey 最容易犯的一个错误是崩溃发生时没有抓到现场日志。monkey 崩溃通过 logcat 可以看到堆栈信息,但 logcat 缓冲区是滚动的,如果你没有提前做好过滤和归档,等到跑完再去翻日志,最早的崩溃信息早就被冲掉了。离线脚本的好处在于,它能实时监听崩溃事件,一旦发现异常就立刻抓取adb bugreport、logcat以及/data/anr/目录下的 trace 文件,把现场固定下来。

第三个痛点是可复现性。monkey 最大的特点是随机,但随机不代表无法控制。每条随机事件序列都基于一个 seed 值生成,只要使用相同的 seed 和相同的命令参数,就能在另一台设备上复现同样的操作序列。离线脚本的核心价值之一,就是把 seed、事件数、事件百分比固定下来,保证每次回归的随机序列可追溯、可复现。不然这次的崩溃是随机事件击中某个深藏 bug 导致的,你下次随便跑跑不出来,这个 bug 就会被漏掉。

1.2 离线脚本适合哪些场景

从场景划分来看,monkey 离线脚本最常用的有三类用途。

一类是版本发布前的稳定性验证。新版本提测后,测试团队拿一台或一组设备,在构建产物上跑一整夜的高强度 monkey,观察有没有内存泄漏崩、ANR 频发、IPC 异常这类问题。这类场景对日志完整度和结果分类要求很高,因为需要快速定位是哪个模块引发的异常。

另一类是竞品对比和回归监控。有的团队会把某应用的历史版本、竞品版本放到同一批设备上,用相同 seed、相同事件流跑同样的时间,然后对比各自的崩溃率、ANR 次数、内存占用曲线。这种对比测试如果靠人肉操作几乎不可能完成,而脚本化之后只需要在配置里切换 APK 路径和包名即可。

再有一类是 CI/CD 流水线的集成。很多团队已经把 monkey 离线脚本作为自动化测试任务的一部分,打包阶段完成后自动触发一轮烟雾级的 monkey 测试,例如跑 1 万事件,如果崩溃率超过阈值,流水线直接标红。这种场景下脚本不需要交互,只需要一个命令行入口、一个固定的退出码和一份机器可读的报告。

2. 技术底座:把 monkey 命令参数吃透

2.1 参数不是随便填的,先理解玩法的可组合性

monkey 的命令格式虽然简单,但参数组合非常多,不同组合的测试目的完全不同。很多新手喜欢直接adb shell monkey 50000,把默认参数跑满,最后崩溃一堆,也不知道是哪个动作触发的。我建议在做离线脚本之前,先把下面这张常用参数表过一遍,按真实需求去选。

参数作用实际用法
-p指定包名-p com.demo.app限定只测试目标应用,不写会整个系统乱跑
-s指定种子值-s 1687保证随机序列可复现
--throttle事件间隔毫秒--throttle 300表示每个事件之间停顿 300ms
-v日志详细级别-v -v -v输出最详细的事件、崩溃信息
--pct-touch触摸事件占比--pct-touch 40设置触摸事件占 40%
--pct-motion滑动事件占比--pct-motion 25
--pct-appswitch应用切换占比--pct-appswitch 10模拟用户后台切换
--pct-syskeys系统按键占比--pct-syskeys 5模拟 Home、返回等按键
--ignore-crashes崩溃后继续运行遇到崩溃不中止,跑完整个事件流
--ignore-timeoutsANR 后继续运行遇到 ANR 不中止
--kill-process-after-error出错后杀死应用进程设置后下次事件从新进程开始
--monitor-native-crashes监听原生层崩溃监控 so 层崩溃需要加上
--bugreport崩溃时自动抓取报告每次崩溃自动尝试抓取adb bugreport
--dbg-no-events不发送事件,仅启动应用可用于初始化安装验证

这些参数看起来零散,但是组合起来就是测试策略。比如我需要做一次偏向用户操作习惯的标准回归,我会把触摸和滑动压到 60% 以上,同时留一部分给应用切换和系统按键,来模拟真实用户频繁切换页面的过程。如果我是偏向做异常状态验证,比如来电打断、低电量提示这类场景,就要调高--pct-syskeys和--pct-appswitch的占比,同时配合其他指令。

2.2 用 seed 锁定随机序列,把概率变成可复现

seed 可以说是 monkey 测试里最容易被忽略但是最重要的参数。我的经验是,任何一次正式测试都要显式指定 seed。为什么要这么做?因为 monkey 的随机事件发生器是一个伪随机算法,给定相同的 seed,它生成的事件序列是相同的。种子不同,跑的路径完全不同,很多崩溃只有特定的种子序列才能触发。

举个例子。假设当前版本在手势滑动某个页面时偶发崩溃,但你随机跑 10 万事件没有复现,这时候最好的做法不是加大数量盲目撞概率,而是保住当前现场的 seed。现场日志里会记录当时用的 seed 值,下次直接用这个 seed 搭配相同的参数和包名版本重新跑,大概率可以复现问题,然后交给开发定位。离线脚本里必须把这个 seed 自动化落盘,并且报告里带上有用的现场索引。

我在工作里习惯约定 seed 的生成策略:如果是日常轮询回归,用固定的常用 seed,比如 10086,保证每轮测试事件序列一致,方便横向对比;如果是独立验证测试,使用时间戳生成 seed,避免每次跑的都是同一条路径,发现问题的覆盖面更广。无论哪种策略,最后的报告里一定要能追溯到 seed 和完整命令。

2.3 事件百分比设置的思路,不是拍脑袋

事件百分比直接决定测试压力作用在哪些交互行为上。很多人偷懒直接不写--pct-*,结果系统默认的事件分布跟你期望的完全不一样。默认 monkey 的触摸、滑动等事件占比是各占一部分,但系统按键和导航事件也占有相当比例,实际体验下来会感觉操作很“乱”,容易误触一些系统界面。

我的建议是先用执行任务表把期望的事件分布写清楚,再换算成百分比。假设我要做一次电商类应用的稳定性验证,用户行为主要集中在浏览商品、滑动列表、点击加购、切换 Tab、偶尔退出到桌面。那么对应的事件模型,我会设置触摸 45%、滑动 30%、应用切换 10%、系统按键 5%、其他事件(缩放、旋转等)10%。这样跑出来的路径比较接近真实用户的操作节奏,而不是像无头苍蝇一样在系统各个页面乱跳。

如果目标是验证多进程或者后台服务稳定性,我会降低触摸和滑动的占比,提高系统按键和应用切换占比,模拟用户反复切后台、唤起其他应用,观察目标进程是否被杀、是否产生 ANR。不同测试目标要匹配不同的参数模板,这套模板要提前沉淀在离线脚本的配置文件中,而不是每次临场手敲。

3. 脚本设计:拆成模块之后,逻辑一下就清楚了

3.1 离线脚本应该具备哪些能力

很多人以为一个 monkey 离线脚本就是一行adb shell monkey加一个日志重定向,这个理解太浅了。真正能上生产的脚本,至少应该具备下面几项能力。

第一是设备连接管理。要能自动检测连接的设备,支持指定序列号、多设备并发、设备断开后的重试逻辑。第二是前置条件处理。安装指定 APK、设置权限、清理应用数据、关闭系统自动更新等干扰因素。第三是 monkey 执行控制。包括命令构造、事件数量、seed、超时控制、执行状态统计。第四是日志采集与归档。实时抓取 logcat、崩溃栈、ANR trace、bugreport等,按时间戳和场景目录归档。第五是报告生成。用统一格式输出执行概要、崩溃列表、崩溃详细信息、内存趋势等,方便后续人工判断和团队周报。

基于这个清单,我在实际项目里通常把离线脚本拆成两个大的部分:负责跑流程的外层控制脚本,和负责数据后处理的 Python 分析模块。外层控制脚本用 shell 或者批处理编写,在 Linux 执行机上负责多设备调度、超时控制、简单轮询、日志粗分类;Python 部分负责解析logcat里的崩溃关键字、聚合 ANR 信息、生成可读的 HTML 文本报告。这套结构的好处是逻辑清晰,外壳坏了可以直接改壳,分析逻辑变更不需要动执行层。

3.2 日志目录设计和文件名规则的讲究

日志目录是我踩过最多坑的地方。没有一套规范的目录,日志采集回来以后根本没法快速定位问题。我现在的标准做法是,在每个设备的测试根目录下按设备序列号_日期_测试轮次建目录,内部再细分子目录。

output/ 192.168.1.10_20250614_001/ installd.log # 安装与前置步骤日志 monkey.log # monkey 标准输出 logcat_main.log # 主 logcat 全量日志 logcat_crash.log # 实时过滤的崩溃日志 events.log # monkey --bugreport 生成的事件细节 traces/ # ANR trace 文件 001_crash_com.demo.app.log 001_anr_com.demo.app.log report.txt

这个结构的好处是,后续无论是人工排查还是写自动化分析工具,都能基于确定的路径找到对应文件。文件名加了序号后,崩溃发生的顺序一目了然。我曾经遇到一个团队把崩溃日志全部堆到一个目录里,文件名又是纯时间戳,排查问题时翻了整整一小时,效率极低。

3.3 崩溃监听和 ANR 采集要主动抓,别只依赖重定向

这是整个离线脚本里技术含量最高的部分。直接把adb shell monkey的输出重定向到文件只是最基础的日志保存,真正关键的是实时日志采集。我通常会在脚本里同时开一个后台循环,持续执行adb logcat -v threadtime抓取实时日志,并且在抓取的同时用关键词过滤崩溃信息。

常见的关键词包括FATAL EXCEPTION、ANR in、am_anr、am_crash、嵌入式场景还要看SIGSEGV、SIGABRT这类信号量。一旦检测到崩溃,立即复制当前logcat缓冲区的关键片段,同时尝试从设备上拉取/data/anr/目录下新的 trace 文件。这里要注意,抓取 ANR 文件需要 root 权限或者使用adb bugreport来间接获取,直接在 shell 里adb pull /data/anr/经常会失败,这也是后面问题排查要展开的部分。

这样的主动监听方式比等 monkey 跑完再统一分析要好得多,因为崩溃现场有大量瞬时信息,比如事件序列、内存快照、CPU 状态,过了这个时机就再也拿不到了。

4. 实操过程:一份能直接上手的 monkey 离线脚本

4.1 前置准备与运行环境

动手写脚本之前,先把执行环境准备好。macOS 或者 Linux 上安装好 adb 工具,Windows 上建议在 PowerShell 或者转换为 shell 兼容环境运行,不过我当时直接用的 Ubuntu 执行机,配合多台 Android 设备通过 USB 或者 Wi-Fi adb 连接管理。执行前确认设备开启开发者模式,并且已授权adb调试,运行adb devices能看到所有设备。

为了让脚本可以灵活切换测试目标,我在脚本目录下放了一个config.env配置文件,集中管理包名、APK 路径、事件数、seed、事件百分比、执行时长上限这些参数。离线脚本的灵活性全部归功于把可变项收敛到配置文件中,不用为了每类测试都去改代码本体。

PACKAGE=com.demo.app APK_PATH=./app-release.apk EVENT_COUNT=50000 SEED=10086 THROTTLE=300 TOUCH_PCT=45 MOTION_PCT=30 APP_SWITCH_PCT=10 SYSKEYS_PCT=5 TIMEOUT_SEC=7200

4.2 脚本主体:shell 部分的管理与控制逻辑

外层脚本主要完成设备选择、APK 安装、日志目录准备、monkey 命令构造、超时控制和基础日志轮转。这里有一个关键点是,执行 monkey 命令时要使用timeout命令包裹,防止因为某些异常事件导致进程挂起而无限等待。monkey 自身有事件数量限制,但事件之间如果某个--throttle值设置得太小,遇到页面动画卡顿,事件注入速度会受影响,实际耗时会比你预想的长很多。超时控制必须要有,否则 CI 任务会把执行机长时间占住。

#!/bin/bash source ./config.env ADB="adb" DEVICE_ID=$1 if [ -z "$DEVICE_ID" ]; then echo "Usage: $0 <device-id>" exit 1 fi # 输出目录构建 BASE_DIR="./output/${DEVICE_ID}_$(date +%Y%m%d_%H%M%S)_${SEED}" mkdir -p "$BASE_DIR/traces" run_cmd() { "$ADB" -s "$DEVICE_ID" $@ } # 安装与系统预处理 run_cmd install -r "$APK_PATH" > "$BASE_DIR/installd.log" 2>&1 run_cmd shell am force-stop "$PACKAGE" run_cmd shell pm clear "$PACKAGE" # 构造 monkey 命令参数 CMD="$ADB -s $DEVICE_ID shell monkey -p $PACKAGE -s $SEED \ --throttle $THROTTLE \ --pct-touch $TOUCH_PCT \ --pct-motion $MOTION_PCT \ --pct-appswitch $APP_SWITCH_PCT \ --pct-syskeys $SYSKEYS_PCT \ --ignore-crashes \ --ignore-timeouts \ --monitor-native-crashes \ -v -v -v \ $EVENT_COUNT" # 启动实时 logcat 采集 run_cmd logcat -c run_cmd logcat -v threadtime > "$BASE_DIR/logcat_main.log" 2>&1 & LOGCAT_PID=$! # 执行 monkey,加超时控制 timeout "$TIMEOUT_SEC" $CMD > "$BASE_DIR/monkey.log" 2>&1 MONKEY_EXIT=$? # 停止 logcat 采集 kill $LOGCAT_PID 2>/dev/null # 基础崩溃关键词提取 grep -n -E "FATAL EXCEPTION|ANR in" "$BASE_DIR/monkey.log" > "$BASE_DIR/crash_hint.log" echo "$MONKEY_EXIT" > "$BASE_DIR/exit_code.txt" exit $MONKEY_EXIT

这段 shell 脚本的思路很直接,安装、清数据、构造命令、后台日志、超时执行、崩溃关键词初筛。执行完以后,立即把退出码和崩溃摘要存到固定位置,方便后续汇总。这里--ignore-crashes和--ignore-timeouts我刻意加上,因为目标不是遇到一次崩溃立刻中止,而是让 monkey 持续跑完整个压力周期,把所有问题都暴露出来。如果你想尽早暴露并停止,可以把这两个参数去掉。

4.3 Python 后处理:从日志到可读报告

Shell 负责跑,Python 负责分析和归档。这部分我写了一个简单的日志解析器,读取crash_hint.log、monkey.log和logcat_main.log,按崩溃类型分类,并尝试提取崩溃发生的具体堆栈片段。核心逻辑是,在logcat_main.log里定位FATAL EXCEPTION出现的位置,然后从该行往后找一段包含at com.demo.app的调用栈,输出到独立文件。

import os import re import sys from collections import defaultdict def parse_monkey_log(monkey_log_path): lines = open(monkey_log_path, encoding="utf-8", errors="ignore").readlines() result = {"crashes": 0, "anr": 0, "finished": 0, "events": 0} for idx, line in enumerate(lines): if "FATAL EXCEPTION" in line or "Process:" in line: result["crashes"] += 1 if "ANR in" in line: result["anr"] += 1 if "Events injected" in line: m = re.search(r"Events injected: (\d+)", line) if m: result["events"] = int(m.group(1)) if "Monkey finished" in line: result["finished"] = 1 return result def extract_crash_stack(logcat_path, output_dir): lines = open(logcat_path, encoding="utf-8", errors="ignore").readlines() count = 0 for idx, line in enumerate(lines): if "FATAL EXCEPTION" in line or "ANR in" in line: count += 1 file_name = f"{count:03d}_crash.txt" target = os.path.join(output_dir, file_name) with open(target, "w", encoding="utf-8") as f: end = min(idx + 80, len(lines)) f.writelines(lines[idx:end]) print(f"[extract] {file_name}") return count if __name__ == "__main__": base = sys.argv[1] stats = parse_monkey_log(os.path.join(base, "monkey.log")) crash_files = extract_crash_stack(os.path.join(base, "logcat_main.log"), os.path.join(base, "traces")) with open(os.path.join(base, "report.txt"), "w", encoding="utf-8") as f: f.write(f"crashes: {stats['crashes']}\n") f.write(f"anr: {stats['anr']}\n") f.write(f"events: {stats['events']}\n") f.write(f"finished: {stats['finished']}\n") f.write(f"extracted_crash_files: {crash_files}\n")

这段代码有个细节:崩溃栈片段提取不是简单地把整段日志都复制到崩溃文件,而是只取崩溃点之后的 80 行。因为 logcat 在崩溃发生后往往紧接着出现大量新事件,太多无关日志会干扰定位。80 行是我在实际使用中觉得比较合适的窗口,基本覆盖从FATAL EXCEPTION到at com.demo.app的完整 Java 堆栈,又不至于把后续无关日志带进来。

4.4 多设备并发的调度思路

生产环境里很少只跑一台设备,多设备并发是标配。最简单可靠的并发方案是循环逐个设备执行上面那个 shell 脚本,但这里有个问题:adb 多设备命令不加-s参数时,它会报错或者默认选择第一台设备,造成执行混乱。需要在最外层写一个调度脚本来遍历adb devices输出的设备列表,并为每个设备启动一个后台任务。

#!/bin/bash adb devices | awk 'NR>1 {print $1}' | while read dev; do if [ "$dev" != "" ]; then echo "start monkey on $dev" ./run_monkey.sh "$dev" & fi done wait echo "all device tasks completed"

多设备并发时要额外注意执行机的串口带宽和 USB 资源。如果设备数量较多,建议给每台设备的 adb 连接加一个server级配置,同时限制同时拉取日志的数量。日志拉取是 IO 密集操作,我曾经一次并发 16 台设备拉取 bugreport,结果把执行机的磁盘 IO 打满,所有任务的写入速度都明显下降。后来我在调度脚本里对设备分批处理,同一批次最多 4 台进入日志拉取流程,其他设备先等待,整体效率反而更高。

5. 常见问题与排查技巧实录

5.1 monkey 跑完了,日志目录里却什么都没有

遇到这个问题,第一反应不是脚本出问题了,而是先看设备是否真的执行了命令。很多情况下是因为安装失败或者包名不匹配导致 monkey 根本启动不了目标应用。检查installd.log是否有Success,再手动执行一下adb shell monkey -p 包名 1看返回输出。我遇到过一次比较隐蔽的问题是,测试 APK 和当前设备 Android 版本的 targetSdk 冲突,安装能够成功,但启动时直接崩溃,monkey 还没开始注入事件应用就已经死了。这时候 monkey.log 里通常会有Error launching activity的字样。

另一个常见原因是 logcat 后台进程没有正确启动。你要确认脚本里adb logcat是否用了后台&符号,以及执行完 monkey 后是否把该进程 kill 掉。如果上一轮的 logcat 进程没清理,当前轮的日志会写到上一个进程对应的文件,你打开当轮目录自然什么都看不到。

5.2 崩溃日志抓到了,但 ANR trace 始终拉取失败

这是没有 root 的设备上最常见的坑。Android 系统的/data/anr/目录权限受限,普通 adb 权限无法直接访问。很多测试员在脚本里写adb pull /data/anr/然后发现 Permission denied,就以为拿不到 ANR 现场了。

其实可以用两个替代方案。第一个是在 monkey 命令里带--bugreport参数,崩溃或 ANR 时可以自动触发 bugreport 收集/data/anr/下的信息。第二个是升级为adb bugreport手动采集,它能把系统所有诊断信息打包到执行机,里面通常包含traces.txt。我的离线脚本在检测到 ANR 关键词时,会额外调用一次adb bugreport来导出系统 snapshot,虽然耗时较长,但采集到的信息量比单独拉 ANR 目录全面得多。要注意的是,adb bugreport本身耗时可能超过一分钟,如果 ANR 触发频繁,采集计划要做好超时和频率控制,避免日志还没拉完设备已经被下一轮事件继续冲击。

5.3 monkey 命令卡住不动,超时后杀不掉进程

有时候timeout虽然能杀掉 adb 命令,但设备里的 monkey 进程可能还在跑,继续消耗系统资源。此时你要在 shell 脚本的超时处理分支里补一步清理设备端进程:

adb shell pkill -f monkey

但注意,pkill不是所有 Android 设备都内置。更通用的做法是,先查 monkey 进程的 PID,再adb shell kill -9 PID。我一般会在脚本里封装一个函数,在timeout返回非零退出码时,主动执行一次进程清理。这样下一轮测试开始前设备是干净的,不会出现前一个 monkey 的事件流还留在系统里,干扰新一轮测试的情况。

5.4 崩溃率很高,但人工复看日志发现都是同一个误报

monkey 崩溃日志里的内容不都是应用代码的 bug,也可能来自系统组件。比如某些设备在低内存条件下会触发系统桌面重启,日志里会看到systemui的 FATAL EXCEPTION,但它跟目标应用无关。离线脚本的分析模块一定不能被所有崩溃关键词骗到,要在报告里按进程名分类统计。

我的做法是在extract_crash_stack里把崩溃进程名也解析出来,与目标包名做匹配。只把与目标包名相关的崩溃标记成有效崩溃,系统级崩溃单独列一份统计便于处理。这样做能大幅减少报告噪音。实际项目里我见过有团队因为统计了系统桌面崩溃,误报率高达 60%,开发复查后全是无效单,后面自动化可信度受到很大影响。

现象排查方向处理建议
日志目录为空adb 连接、安装失败、包名不匹配检查 installd.log、单步手动执行
ANR 无法拉取/data/anr 权限不足使用 --bugreport 或 adb bugreport 兜底
monkey 卡死事件流异常、低内存timeout 包裹,pkill 进程清理
崩溃率虚高系统进程崩溃被统计按进程名过滤,只统计目标包
多设备并发拉日志慢USB/磁盘 IO 瓶颈分批拉取、限制同批设备数量

5.5 报告里的退出码含义要准确解读

monkey 命令退出码不是随随便便看的。按照我的经验,退出码 0 表示 monkey 执行完毕,退出码非 0 则多半是发生了中断或者错误,但这里有个细节:即使加了--ignore-crashes,monkey 仍然可能因为其他异常返回非零退出码。例如设备存储满导致日志写不进去、adb 连接断开、设备进入休眠锁屏等,都会让命令提前结束。你不能简单认为退出码非 0 就是被测应用出现了崩溃,应该结合monkey.log里的Monkey aborted due to error这类文本做二次确认。

这里分享一个排查技巧:在日志里搜索** Monkey aborted due to error后面紧跟的原因字段,它通常直接告诉你是什么触发了中断。我之前遇到过几次设备屏保导致事件无法注入,就是通过这个字段定位到的。处理方法是提前设置设备亮屏时间无限长,并在脚本执行前通过svc power stayon true强制保持唤醒。

5.6 顺手总结三个独家小技巧

脚本里所有临时文件命名都要带设备和 seed,别只带时间戳。多设备并发状态下,不同设备的日志如果文件名重复,输出目录一合并直接互相覆盖,这类事故发生过不止一次。

monkey 的阈值和判定标准要固化,不要每次跑完再人肉定标准。最好的方式是在配置里写清楚“崩溃次数大于等于 1 就标红”,同时允许人工在后处理报告里备注无效崩溃。自动化结果如果不带人工复核通道,时间长了大家会对红色告警麻木,最终这个工具会被团队弃用。

日志分析时不要忽略Events injected这个字段。它显示实际注入的事件数量。如果配置问 5 万事件,但它只注入了 1 万就收到系统中断,说明测试并没有达到预期压力强度,即使是退出码 0 也要引起注意。

6. 这套脚本后续还能往哪个方向扩展

monkey 离线脚本做到一定程度之后,瓶颈往往不在脚本本身,而在如何把结果和研发流程打通。我目前比较喜欢的方向是把离线脚本的产出标准化为 JSON 格式,然后对接团队内部的看板系统,每次测试完成自动推送一条消息,包含崩溃摘要、趋势变化、复跑入口。这样稳定性测试的结果不只是跑完自己看看,而是能实时暴露给整个团队。

另一个扩展方向是接入内存监控。压力测试如果只看崩溃和 ANR,很多内存泄漏问题会被漏掉,因为这些泄漏往往不会在短时间内触发崩溃。可以在外部脚本里周期性执行adb shell dumpsys meminfo 包名,记录内存曲线,跑完以后对照事件流定位是哪个阶段导致的内存激增。这块我当时没有写进第一版脚本,后面单独花了一个迭代周期补上,效果很好。

如果你所在的团队已经有统一调度平台,还可以把这套 shell 脚本封装成可执行任务,做成参数化驱动。比如传入 APK 下载地址、包名、测试时长,平台自动分配设备和存储空间。脚本本身保持简单,把复杂调度交给平台,脚本只负责“跑设备相关的脏活累活”。

回到最开始的那个问题。monkey 离线脚本本质上是把测试员从重复劳动里解放出来的工具,但工具能发挥多大价值,取决于使用者对 monkey 参数、日志语义和设备行为的理解深度。能跑通一条命令很容易,能把崩溃现场完整地带回来并在团队协作中形成闭环,才算真的把离线脚本做成了项目里靠谱的基础设施。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 7:01:20

题霸刷题指南:如何用题解汇总高效备战算法面试

如果你也在准备技术面试&#xff0c;大概率听过"题霸"这个名字。我第一次点开题霸的题目列表时&#xff0c;心里其实有点不屑&#xff1a;这不就是一个算法题库吗&#xff1f;后来刷了两周才反应过来&#xff0c;它压根不是普通的题库&#xff0c;更像一本被拆开揉碎…

作者头像 李华
网站建设 2026/10/15 7:01:11

Unity单元测试最佳实践:NUnit与Test Runner深度集成指南

简介&#xff1a;本资源是面向Unity游戏开发者的NUnit单元测试框架实战指南包&#xff0c;专为中高级开发者设计&#xff0c;解决Unity项目中逻辑验证难、Bug定位慢、迭代信心不足等核心问题。压缩包共1504个文件&#xff0c;以906个C#测试脚本&#xff08;含[Test]和[TestFixt…

作者头像 李华
网站建设 2026/10/15 7:00:51

AnyPS5跨端串流实战:延迟优化与画质调校指南

1. 从“AnyPS5”这个名字说起&#xff1a;它到底想解决什么问题第一次看到“AnyPS5”这个标题&#xff0c;我脑子里冒出来的第一个念头是&#xff1a;这大概率不是一个单纯的硬件改装项目&#xff0c;而是一个围绕“跨平台、跨设备、跨场景”做文章的工具型项目。为什么这么说&…

作者头像 李华
网站建设 2026/10/15 7:00:41

电信开源TeleOCR:1.2B参数文档解析模型,ICDAR科学图解析冠军方案解析

1. 从一张科学图表说起&#xff1a;为什么文档解析远不止“认字”这么简单如果你做过文档数字化相关的项目&#xff0c;大概率经历过这样的场景&#xff1a;一张学术论文里的折线图&#xff0c;横轴是温度、纵轴是转化率&#xff0c;图例区分了三种催化剂&#xff0c;旁边还有一…

作者头像 李华
网站建设 2026/10/15 7:00:35

从记忆里导航:用LLM和RAG实现可靠路线规划的关键实践

有人让我用大语言模型做"Driving directions"&#xff0c;一开始我以为听错了——让LLM开车指路&#xff1f;后来想明白了&#xff0c;这里的关键其实不是"方向"本身&#xff0c;而是"memory"这个词。LLM怎么从它庞大的记忆里&#xff0c;把一条…

作者头像 李华
网站建设 2026/10/15 7:00:32

Java进阶核心路线:吃透JVM原理与并发实战

干了这么多年Java&#xff0c;经常看到类似的标题&#xff1a;"从基础到高级""Java进阶必看"等等。但这个标题有一点说得很对&#xff1a;Java进阶确实没那么难&#xff0c;难的是大多数人一开始就把路走偏了。上来就啃《深入理解Java虚拟机》&#xff0c;…

作者头像 李华