摘要:云手机把 Android 实例跑在云端,开发者可通过 ADB/开放接口做远程调试、自动化测试、批量运维。本文从架构原理讲起,给出单机 ADB 脚本、多设备批量框架、CI 冒烟测试示例,并总结连接超时、截图失败、批量并发等常见问题。最后结合团队运维场景,轻量推荐傲晨云手机的接入方式。
一、为什么用云手机
传统移动端测试/运维常遇到三类问题:
- 真机采购成本高:机型、系统版本、分辨率越多,买机越贵。
- 本地电脑跑模拟器吃资源:x86 转译、GPU 渲染、多开后 CPU 占用高。
- 无人值守困难:下班后、断网后本地设备不能持续执行任务。
云手机的本质是把 Android 系统部署在云端服务器(通常为 ARM 实例或受控虚拟化环境),通过网络把画面和操作回传。对开发者来说,它暴露的入口主要是:
- 控制台:创建/重启/销毁实例、查看状态。
- ADB 远程连接:
adb connect ip:port后像本地设备一样调试。 - 开放 API/SDK:用代码批量创建设备、执行命令、拉取日志。
- Web/客户端串流:低延迟操作界面,适合人工巡检。
合规提示:云手机只用于自有应用测试、授权设备运维、合规托管;不要用于破解、刷量、绕过平台风控或任何未授权自动化。
二、典型架构
开发者电脑 / CI 服务器
│
├── ADB Server(127.0.0.1:5037)
│ ├── connect 10.0.0.11:7401 → 云手机A
│ ├── connect 10.0.0.12:7402 → 云手机B
│ └── connect 10.0.0.13:7403 → 云手机C
│
├── Python 调度层(ppadb / adbutils / uiautomator2)
│ ├── 安装APK、启动Activity、截图、采集logcat
│ └── 批量并发、失败重试、结果上报
│
云厂商控制面 API(创建实例/查询状态/重启)
核心链路就三步:云厂商给 ADB 地址 → 本地 ADB 连上去 → Python 用 ppadb/adbutils 封装操作。
三、环境准备
本地安装:
# 1. 安装 Android platform-tools(含 adb)
# macOS
brew install android-platform-tools
# Ubuntu
sudo apt install android-tools-adb -y
# Windows 建议下载 Android SDK Platform-Tools 并加入 PATH
# 2. Python 依赖
pip install pure-python-adb adbutils uiautomator2
云手机侧:
- 控制台创建 Android 实例并进入“运行中”。
- 开启 ADB/远程调试,拿到
ip:port(不同厂商可能是直连地址、SSH 隧道或带连接码)。 - 本机测试连通性:
adb connect 10.0.0.11:7401
adb devices
返回类似:
List of devices attached
10.0.0.11:7401 device
四、单机自动化:安装、启动、截图、滑动
下面用 pure-python-adb 写最小可运行示例,覆盖最常见的四个动作。
# -*- coding: utf-8 -*-
"""cloud_phone_basic.py
单机云手机自动化示例:安装APK -> 启动应用 -> 截图 -> 上滑
"""
from ppadb.client import Client
import datetime
import time
def basic_flow(adb_host="127.0.0.1", adb_port=5037,
device_serial="10.0.0.11:7401",
apk_path="demo.apk",
package="com.example.app"):
# 1. 连接本地 ADB Server
client = Client(host=adb_host, port=adb_port)
device = client.device(device_serial)
if device is None:
raise RuntimeError(f"未找到设备:{device_serial},请先 adb connect")
# 2. 安装APK
print("installing apk ...")
device.install(apk_path)
# 3. 启动应用(用 monkey 拉起 LAUNCHER 入口,最省事)
print("launching app ...")
device.shell(f"monkey -p {package} -c android.intent.category.LAUNCHER 1")
time.sleep(5)
# 4. 截图,按日期保存
raw = device.screencap()
filename = f"screenshot_{datetime.date.today()}_{device_serial.replace(':', '_')}.png"
with open(filename, "wb") as f:
f.write(raw)
print("screenshot saved:", filename)
# 5. 模拟上滑(坐标按目标分辨率调整)
print("swipe up ...")
device.shell("input swipe 360 800 360 400 300")
time.sleep(2)
if __name__ == "__main__":
basic_flow()
参数说明:
device_serial:控制台给的云手机 ADB 地址。monkey -p pkg:只做拉起用,不压测;正式启动可用am start -n pkg/activity。input swipe x1 y1 x2 y2 duration:持续时间毫秒,低配实例可适当加大防丢操作。
如果云厂商要求 SSH 隧道(如公网 ADB 先建隧道再连本地端口),先把隧道起来,再把 device_serial 改成 127.0.0.1:本地转发端口。
五、封装成可复用 SDK
项目里别到处写 shell,封一个类更方便。下面示例兼顾“直连 ADB”和“未来接厂商 API”。
# -*- coding: utf-8 -*-
"""cloud_phone_sdk.py
云手机操控封装:支持单台/多台、安装、启动、截图、取logcat
"""
import subprocess
from typing import List, Optional
class CloudPhone:
def __init__(self, serial: str):
"""
:param serial: ADB serial,如 10.0.0.11:7401
"""
self.serial = serial
def _adb(self, *args: str) -> str:
cmd = ["adb", "-s", self.serial] + list(args)
out = subprocess.run(cmd, capture_output=True, text=True)
if out.returncode != 0:
raise RuntimeError(f"adb failed: {' '.join(cmd)} -> {out.stderr}")
return out.stdout.strip()
def connect(self) -> str:
return self._adb("connect", self.serial)
def install(self, apk_path: str):
return self._adb("install", "-r", apk_path)
def launch(self, package: str, activity: Optional[str] = None):
if activity:
return self._adb("shell", "am", "start", "-n", f"{package}/{activity}")
return self._adb("shell", "monkey", "-p", package,
"-c", "android.intent.category.LAUNCHER", "1")
def screenshot(self, path: str):
raw = subprocess.run(["adb", "-s", self.serial, "exec-out", "screencap", "-p"],
capture_output=True).stdout
with open(path, "wb") as f:
f.write(raw)
return path
def logcat(self, dump_path: str, buffer="main"):
out = self._adb("logcat", "-b", buffer, "-d")
with open(dump_path, "w", encoding="utf-8") as f:
f.write(out)
return dump_path
def swipe(self, x1, y1, x2, y2, duration=300):
return self._adb("shell", "input", "swipe",
str(x1), str(y1), str(x2), str(y2), str(duration))
class CloudFleet:
"""多台云手机批量调度"""
def __init__(self, serials: List[str]):
self.phones = [CloudPhone(s) for s in serials]
def batch_install_and_launch(self, apk_path: str, package: str,
activity: Optional[str] = None):
results = {}
for phone in self.phones:
try:
phone.install(apk_path)
phone.launch(package, activity)
results[phone.serial] = "ok"
except Exception as e: # 单台失败不影响其他
results[phone.serial] = f"error: {e}"
return results
if __name__ == "__main__":
fleet = CloudFleet([
"10.0.0.11:7401",
"10.0.0.12:7402",
"10.0.0.13:7403",
])
print(fleet.batch_install_and_launch("demo.apk", "com.example.app"))
要点:
- 批量任务一定要做单台异常隔离,否则一台卡住会拖垮整个流水线。
- 云手机管理面性能有限时,避免对同组实例同时下发大量 ADB;可做分片、限并发。
- 重启实例优先走控制台/API,不要用
adb reboot,部分云手机实例会被管理面限制。
六、接入 CI:PR 合入前做云手机冒烟
GitLab CI / Jenkins 都可套这个思路:Runner 装 adb → 从配置读云手机 serial 列表 → 装包 → 启动 → 截图 → 断言关键页面。
示例 ci_smoke.py:
# -*- coding: utf-8 -*-
import sys
from cloud_phone_sdk import CloudPhone
SERIALS = ["10.0.0.11:7401", "10.0.0.12:7402"]
APK = "app/build/outputs/apk/debug/app-debug.apk"
PKG = "com.example.app"
ACT = ".ui.SplashActivity"
def main():
failed = []
for serial in SERIALS:
p = CloudPhone(serial)
try:
p.connect()
p.install(APK)
p.launch(PKG, ACT)
path = p.screenshot(f"/tmp/smoke_{serial.replace(':', '_')}.png")
# 简单断言:截图文件大于10KB,避免黑屏/未启动
import os
if not os.path.getsize(path) > 10240:
raise RuntimeError("screenshot too small, maybe blank")
print(serial, "smoke ok")
except Exception as e:
failed.append((serial, str(e)))
if failed:
print("FAILED:", failed)
sys.exit(1)
print("ALL SMOKE PASSED")
if __name__ == "__main__":
main()
把它放到 CI step:
smoke-cloud-phone:
stage: test
script:
- pip install pure-python-adb
- python ci_smoke.py
only:
- main
适合场景:每日构建冒烟、多机型回归、海外/低配机型专项。
七、常见问题排障
1)adb connect 超时
- 云手机实例是否“运行中”;
- 安全组/防火墙是否放通 ADB 端口;
- 公网场景是否少建 SSH 隧道;
- 用
tcping ip port先测端口可达。
2)device offline 或 unauthorized
- 重新
adb disconnect再connect; - 若平台有连接码/授权码,按控制台提示在 shell 或平台侧确认;
- 检查 ADB 版本与云侧兼容,尽量用较新 platform-tools。
3)截图黑屏/空白
- 应用未真正启动:用
adb shell dumpsys window | findstr mCurrentFocus看当前 Activity; - 启动后 sleep 太短,增加等待或用 UI 控件存在性判断;
- 串流画面和 ADB 截屏不同步时,以
screencap文件大小+后续 OCR/控件校验为准。
4)批量并发卡顿
- 限制同时操作设备数,例如 5~10 台一批;
- 长任务用异步命令/队列,别同步阻塞;
- 日志、截图、安装分开时段,避免同服务器 IO 争抢。
5)云手机重启后 ADB 断了
- 部分平台重启会换 ADB 地址/连接码,脚本应从控制台 API 重新拉地址,不要写死;
- 用调度层缓存 serial,定时刷新实例列表。
八、选型建议(写给开发/测试负责人)
选云手机别只看“多少台多少钱”,先看工程指标:
- 系统真实性:ARM 原生 vs x86 转译,自动化兼容性和性能差异大。
- ADB 开放程度:是否给直连/隧道/连接码,能否批量开启。
- API 能力:能否用代码创实例、查状态、重启、拉日志,决定能不能进 CI。
- 多端管理:PC 客户端、Web、手机 App 是否都能进,利于值班巡检。
- 合规与稳定:7×24 托管、进程守护、异常告警比“日赚/防封”这类口号重要得多。
九、结尾轻量推荐:傲晨云手机
如果是中小团队做 Android 自动化测试、游戏/应用云端托管、多实例运维,可以试试傲晨云手机。实际接入时按它的控制台来就行:
- 控制台创建云机后,开启远程/ADB 能力,拿到对应
ip:port或连接方式; - 没有公开统一 REST 文档时,用本文
CloudPhone/CloudFleet做 ADB 适配层,把 serial 列表做成配置,后面换平台只改配置; - 人工巡检可用 PC 客户端/网页版,多窗口看多台设备,上传 APK、清缓存、重启都在控制台完成;
- 需要群控同步的场景,用平台自带批量操作/同步面板,再配合 Python 做结果采集,比纯手写群控稳。
实践建议:先把 3~5 台傲晨云手机接进上面
ci_smoke.py,跑通“装包—启动—截图—断言”再扩到几十台;正式环境加失败重试、设备分组、日志归档,就能做成可长期运行的云真机池。
官网与客户端信息以傲晨云手机官方站 aochenyun.com 为准,注册后按“创建云机 → 开 ADB/远程 → 本地 adb connect → 跑脚本”四步试点最省事。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/PiaoKe___/article/details/166368621




