移动端自动化:多设备并发测试怎么管理才不会把真机环境跑崩

移动端自动化一旦从“单机单设备”走向“多设备并发”,会很快碰到一个现实问题:

不是用例不会跑,而是真机环境越来越不稳定。

最典型的表现通常是这些:

  • 同一条命令偶尔跑到别的设备上
  • 设备昨天还能用,今天跑两轮就开始 offline
  • 截图、日志、录屏和实际失败设备对不上
  • 某个 case 跑挂以后,把后面的设备状态一起污染了
  • 并发一上来,整套环境吞吐没上去,故障反而更多

这类问题本质上已经不是“单条 ADB 命令怎么写”,而是设备管理问题。
如果设备管理没有体系,再好的自动化脚本、再全的日志采集、再多的设备数量,最后都会被环境不稳定抵消掉。

所以这一篇我想讲的是:

多设备并发测试到底怎么管理,才能让真机环境真的可用,而不是看起来设备很多,实际没人敢开并发。

一、多设备并发最容易被低估的问题,不是并发本身,而是“资源争抢”

第一次做移动端并发时,重点很容易先放在“能不能同时起 10 个任务”。
但真实项目里,最先出问题的往往不是线程数,而是资源边界没定义清楚。

在真机环境里,至少有四类资源会被争抢:

  • 设备本身
  • ADB 连接
  • 执行机资源
  • 证据产物目录

只要这几类资源里有一类没隔离好,并发执行就会开始出现诡异问题。

比如:

  • 两个任务误用同一台设备
  • 一个任务重启了 ADB,影响所有设备
  • 截图都写到同一个目录里,最后文件串了
  • 同时拉日志、录屏、截图,把执行机 I/O 打满

所以多设备并发的第一原则不是“把任务并起来”,而是:

先把资源边界和占用关系定义清楚。

二、设备调度的第一件事,不是分配设备,而是先定义设备状态机

做设备调度时,只维护一个“可用设备列表”。这在设备很少时还能勉强工作,但规模一大就会失控。

更推荐的做法是:先把设备状态定义清楚,再做调度。

最起码,会把设备分成下面几种状态:

  • idle:空闲,可分配
  • busy:已被任务占用
  • recovering:正在执行恢复动作
  • quarantine:疑似异常,暂时摘除
  • offline:连接不可用
  • maintenance:人工维护中

这个状态机的价值非常直接。

1. 防止设备重复分配

如果没有 busy 状态,很容易出现两个任务几乎同时拿到同一台设备。

2. 防止坏设备继续流入调度

如果设备刚刚发生:

  • 连续安装失败
  • 截图失败
  • 录屏异常
  • 多次 offline

那它就不应该立即回到可用池,而应该先进入 quarantinerecovering

3. 让调度层和恢复层职责分开

调度层只关心“能不能分配”;恢复层只关心“这台设备能不能被救回来”。
这两个逻辑如果混在一起,系统会越来越难排查。

三、设备占用更适合做成“显式租约”,不能只靠内存变量

这件事非常重要。
一开始做并发执行,会在代码里维护一个“当前已占用设备集合”,看起来很简单,但实际很脆弱。

因为只要发生下面这些情况,就会出错:

  • 任务进程崩了
  • 执行机重启了
  • 调度服务和执行服务不在同一进程
  • 多个 worker 同时抢设备

所以更推荐把设备占用做成显式租约,至少包含这些信息:

  • 设备序列号
  • 任务 ID
  • 占用开始时间
  • 心跳更新时间
  • 占用过期时间

这样做的好处是:

  • 设备归属关系清晰
  • 任务异常退出后可以自动回收
  • 更容易排查“这台设备现在到底被谁占着”

否则一旦环境变复杂,最常见的现象就是“设备明明空着,却没人敢动”,或者“设备看起来空闲,实际背后还有残留任务没清掉”。

四、所有设备命令都必须强制绑定序列号,这是并发环境的底线

这个问题看起来基础,但在多设备环境里是最高频的事故源之一。

只要环境里同时连着两台及以上设备,所有命令都必须强制带:

1
adb -s <serial> ...

这件事不能靠口头提醒,必须在封装层强制。

因为只要漏掉一次,就可能出现:

  • 命令发到默认设备
  • 日志采到别的设备
  • 截图和录屏归档错误
  • 清数据动作误伤另一台设备

单设备环境里这类问题不明显,一上并发就会放大成严重事故。

所以更稳妥的建议一直是:

  • 底层命令封装层强制要求传 serial
  • 没传序列号直接拒绝执行
  • 报告和日志里所有证据都显式带设备标识

五、并发环境最怕的不是设备少,而是设备脏

一开始会把精力放在“再多买几台手机”,但如果设备生命周期管理没做好,设备越多,脏设备也越多,环境维护成本只会跟着上升。

所谓设备脏,通常包括几类情况:

  • 应用残留进程没清掉
  • 上一轮测试账号没退出
  • 权限弹窗残留
  • 录屏和日志文件残留
  • 屏幕停在异常页面
  • 网络状态被前一轮测试改掉

这类问题最麻烦的地方在于,它们不一定每次都导致失败,但会让失败变得越来越随机。

所以每次任务结束后,设备都应该有一套明确的收尾动作,而不是“任务跑完就算结束”。

六、更推荐的设备生命周期:分配前检查、执行中守护、结束后回收

如果想把真机环境跑稳,更倾向把一台设备的执行生命周期拆成三段。

1. 分配前检查

目标是确认这台设备值得被分配。

通常会检查:

  • 是否在线
  • 是否 device 状态
  • 电量是否过低
  • 存储空间是否不足
  • 是否存在异常残留文件
  • 是否有目标应用残留进程

如果这些基础检查都不过,不应该把设备直接投入执行。

2. 执行中守护

目标是避免任务跑到一半时,设备 silently 崩掉。

通常会关注:

  • 设备是否 still online
  • 日志采集是否还在
  • 录屏是否成功启动
  • 应用进程是否还存在
  • 关键命令是否持续超时

的任务失败后才去看设备状态,这太晚了。
真正稳的系统会在执行中持续感知设备健康。

3. 结束后回收

目标是让设备尽量回到标准初始态。

常见动作包括:

  • 停止目标应用
  • 清理临时文件
  • 拉回证据产物
  • 删除设备侧录屏
  • 回到桌面
  • 必要时清应用数据

如果任务结束后没有统一回收,脏状态会不断积累,最终把整个设备池拖垮。

七、设备恢复更适合分层,不能遇到失败就直接重启

在真机环境不稳定时,最常见的补救方式就是“重启手机”。
这个动作不是不能用,但如果把它当成默认恢复手段,成本非常高。

更倾向把恢复分成三层。

1. 轻恢复

适用场景:

  • 页面卡住
  • 应用停在异常页
  • 截图或输入短暂异常

常见动作:

  • 回桌面
  • 强杀目标应用
  • 清理临时文件
  • 重新拉起应用

2. 中恢复

适用场景:

  • 应用数据明显脏掉
  • 多次启动失败
  • 日志和录屏能力异常

常见动作:

  • 清应用数据
  • 重建 ADB 会话
  • 重跑基础健康检查

3. 重恢复

适用场景:

  • 设备频繁 offline
  • 系统卡死
  • 存储异常
  • 连续多轮恢复失败

常见动作:

  • 重启设备
  • 从调度池摘除
  • 标记人工检查

分层的意义在于:不是每个失败都值得付出最高恢复成本。
如果恢复策略过重,并发吞吐会被拖垮;如果恢复策略过轻,脏状态又会继续传播到后续任务。

八、并发执行时,证据也要按设备和任务双重隔离

多设备并发很容易被理解成只要“命令不要串”就够了,但实际另一个高频事故点是证据产物串了。

典型现象包括:

  • A 设备的截图挂到了 B 设备的报告里
  • 一个 case 的日志被另一个任务覆盖
  • 录屏文件名重复,后写覆盖前写

所以多设备环境下,证据目录必须至少按下面两个维度隔离:

  • 设备序列号
  • 任务或 case 标识

例如:

1
2
3
4
5
6
outputs/
├── run_20230203_203000/
│ ├── device_R58N12345AB/
│ │ ├── case_login_001/
│ ├── device_R3CN9088XYZ/
│ │ ├── case_login_001/

这样哪怕同一个 case 同时在两台设备上跑,证据也不会串在一起。

九、执行机资源也要被当成并发约束,不然设备多也没用

做真机并发时,只看设备数量,不看执行机瓶颈。这会导致一个错觉:设备明明足够多,但整体执行速度就是上不去。

常见瓶颈包括:

  • adb 子进程太多
  • 日志采集太多导致 CPU 占用上升
  • 截图、录屏、文件拉取导致 I/O 饱和
  • 大量并发分析日志导致执行机变慢

所以并发上限不能只按设备数来设,还要结合:

  • 执行机 CPU
  • 内存
  • 磁盘 I/O
  • USB Hub 或网络调试链路稳定性

否则你会看到一种很典型的现象:设备数量翻倍了,但故障率也翻倍,实际吞吐反而下降。

十、在项目里踩过的几个真机并发坑

坑 1:设备池里“看起来可用”的设备,其实已经半坏了

现象:

  • adb devices 能看到
  • 偶尔也能执行命令
  • 但安装、截图、录屏成功率明显偏低

如果调度层只看“在线/离线”,这种设备会不断被重新分配,最终制造大量偶发失败。

修复思路:

  • 增加健康分或失败计数
  • 连续异常达到阈值后自动摘除
  • 不让“勉强能用”的设备继续参与调度

坑 2:任务异常退出后,设备一直处于假占用状态

现象:

  • 设备没人真正使用
  • 但调度系统一直认为它被占着

根因通常是没有租约超时和心跳回收。

修复思路:

  • 占用记录加 TTL
  • worker 定期续约
  • 超时未续约则自动释放并进入健康检查

坑 3:恢复动作误伤了别的任务

现象:

  • 某个任务失败后执行了 adb kill-server
  • 结果所有设备一起受影响

这类问题说明恢复动作粒度太粗。
在并发环境里,任何全局性动作都要极度谨慎,因为它可能不是“修一台设备”,而是“把整个执行面一起打断”。

坑 4:问题明明出在环境,却被误判成业务缺陷

这种情况非常常见。
比如:

  • 某台设备存储空间满了
  • 权限弹窗残留
  • 录屏文件没删
  • 某个 USB 口不稳定

最后表现出来可能只是“登录失败”或“元素不存在”,如果没有设备健康上下文,很容易被当成业务问题。

十一、更推荐的一套真机并发管理思路

如果后面要做成平台或稳定的 CI 资产,更推荐至少把能力拆成下面几层。

1. 设备注册与发现层

职责:

  • 发现设备
  • 同步设备元信息
  • 感知在线状态

2. 调度与租约层

职责:

  • 分配设备
  • 维护占用关系
  • 处理续约和超时释放

3. 健康检查与恢复层

职责:

  • 执行分配前检查
  • 执行中监测
  • 失败后恢复
  • 坏设备摘除

4. 任务执行与证据层

职责:

  • 跑用例
  • 采集日志、截图、录屏
  • 按设备隔离证据

把这几层拆开以后,环境才会逐渐稳定。
如果全都堆在一个执行脚本里,问题一多就很难继续演进。

十二、排查真机并发问题时,可以按什么顺序看

当并发环境出问题时,更稳的做法通常不是先去看业务日志,而是按下面顺序排查。

1. 先看是不是设备分配错了

确认:

  • 是否重复分配
  • 是否租约没释放
  • 是否命令带了正确序列号

2. 再看设备自身是不是健康

确认:

  • 是否频繁 offline
  • 是否空间不足
  • 是否存在异常残留文件
  • 是否处于恢复中却被重新分配

3. 再看执行机是否被打满

确认:

  • CPU 是否过高
  • I/O 是否拥塞
  • adb 进程数是否异常

4. 最后再看业务失败本身

因为很多“业务失败”,前面三步就已经能定位出是环境问题。
如果上来先看用例断言,经常会把环境问题误判成产品缺陷。

结语

多设备并发测试真正难的,从来不是“怎么把线程开起来”,而是怎么让设备、任务、证据和恢复动作在并发环境下仍然保持有序。

更关键的是一套真正可用的真机并发管理,至少要做到:

  • 设备状态清晰,不是只有在线和离线
  • 占用关系可追踪,不靠进程内变量硬记
  • 设备命令强制绑定序列号
  • 分配前检查、执行中守护、结束后回收形成闭环
  • 故障设备能被自动摘除,而不是反复污染环境
  • 证据和资源都能按设备隔离

如果这些基础能力没补齐,设备越多,并发越高,环境只会越乱;
只有把调度、隔离、恢复和排障一起做起来,真机环境才会从“能跑”变成“敢长期跑”。