ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

云手机原理与 Python 自动化实战:从虚拟化、ADB 到设备池管理

云手机原理与 Python 自动化实战:从虚拟化、ADB 到设备池管理 摘要云手机不是“远程装个 APK”这么简单核心通常是云端 Android 实例、虚拟化/容器隔离、音视频串流和控制指令回传。本文按原理→架构→代码→踩坑的顺序给出 ADB 与 uiautomator2 两套可运行示例适合做兼容性测试、脚本化巡检和小型设备池管理。文末给出云手机选型建议。一、为什么要用云手机传统移动端自动化有几个常见痛点真机采购成本高Android 版本、分辨率、ROM 碎片化严重本地电脑/手机不能 7×24 开机挂机、定时任务不稳定多设备批量操作时ADB 连接、网络隔离、日志回收都会变复杂。云手机把这些能力放到服务端应用在云端 Android 实例里运行画面编码后推到本地本地只负责看画面和下发点击/按键。对自动化测试、应用兼容验证、轻量托管类任务比较友好。二、云手机核心架构简化分层如下本地客户端/Web/脚本 │ 下行视频流上行触摸/按键/ADB ▼ 流媒体与控制层WebRTC / RTMP / 自定义协议 ADB/uiautomator ▼ Android 实例层每实例独立系统、应用数据、网络命名空间 ▼ 虚拟化/容器层KVM-QEMU 全虚拟化 或 NamespaceCgroup/redroid 容器 ▼ 物理资源层ARM 服务器、vCPU、内存、编码卡/GPU、存储要点虚拟化层强隔离可用 KVMQEMU轻量多开可用 Android 容器如 redroid 思路。ARM 服务器跑 ARM 镜像更贴近真机x86 转译会有额外开销。实例层每个云手机应有独立系统数据、独立网络环境多开场景要避免共享状态导致串号。串流层常用 H.264/H.265 编码WebRTC 做低延迟交互RTMP 适合单向观看。端到端延迟会受网络、编码、分辨率影响。控制层ADB 适合命令行和 CI 集成uiautomator2/Appium 适合 UI 自动化平台开放 HTTP/WebSocket API 时可做批量编排。三、环境准备本地调试环境Python 3.8一台开启 ADB 网络调试的云手机/模拟器/真机安装依赖pip install uiautomator2 adbutils云手机侧需要拿到设备 IP 与 ADB 端口例如10.0.0.21:5555或平台提供的 API 地址、Token被测 APK 包名、主 Activity如有合规提醒只对你拥有、已授权或用于内部测试的设备和应用做自动化不要对第三方账号做批量注册、刷量、绕过安全检测等行为。四、示例一Python 调用 ADB 做通用控制适合做安装、启动、点击、截图、取日志等基础运维。import subprocess from typing import List, Optional class AdbDevice: def __init__(self, endpoint: str): :param endpoint: 云手机 ADB 网络地址如 10.0.0.21:5555 self.endpoint endpoint def _run(self, args: List[str]) - Optional[str]: cmd [adb, -s, self.endpoint] args try: result subprocess.run( cmd, checkTrue, capture_outputTrue, textTrue, timeout120, ) return result.stdout.strip() except subprocess.CalledProcessError as e: print(f[ADB ERROR] { .join(cmd)} - {e.stderr}) except subprocess.TimeoutExpired: print(f[ADB TIMEOUT] { .join(cmd)}) return None def connect(self) - bool: out self._run([devices, -l]) if out and self.endpoint in out: return True # 若未连接先尝试网络接入 subprocess.run([adb, connect, self.endpoint], capture_outputTrue, textTrue) out self._run([get-state]) return out is not None def install(self, apk_path: str) - bool: return self._run([install, -r, apk_path]) is not None def launch(self, package: str, activity: str) - bool: cmd [shell, am, start, -n, f{package}/{activity}] return self._run(cmd) is not None def tap(self, x: int, y: int) - bool: return self._run([shell, input, tap, str(x), str(y)]) is not None def swipe(self, x1: int, y1: int, x2: int, y2: int, duration_ms: int 300) - bool: return self._run([ shell, input, swipe, str(x1), str(y1), str(x2), str(y2), str(duration_ms) ]) is not None def screenshot(self, remote_path: str /sdcard/screen.png, local_path: str ./screen.png) - bool: if self._run([shell, screencap, -p, remote_path]) is None: return False return self._run([pull, remote_path, local_path]) is not None def logcat(self, tag: Optional[str] None, limit: int 200) - Optional[str]: if tag: return self._run([logcat, -d, -t, str(limit), tag]) return self._run([logcat, -d, -t, str(limit)]) if __name__ __main__: device AdbDevice(10.0.0.21:5555) if not device.connect(): raise SystemExit(无法连接云手机请检查 IP、端口和 ADB 网络调试权限) device.install(./demo-app.apk) device.launch(com.example.demo, com.example.demo.MainActivity) device.tap(540, 960) device.swipe(200, 1200, 800, 1200, 300) device.screenshot(local_path./cloud_screen.png) print(device.logcat(tag*:S, limit100))使用说明endpoint换成云手机控制台给出的 ADB 地址若平台只给 Web 控制台不给 ADB则用平台 OpenAPI 替代 shell 命令坐标点击不稳定时优先改用 UI 控件定位见示例二。五、示例二uiautomator2 做 UI 自动化适合界面元素固定、需要按控件 ID/文本操作的测试任务。import uiautomator2 as u2 import time def run_smoke_test(device_endpoint: str, package: str): d u2.connect(device_endpoint) # 1. 唤醒并解锁界面仅测试用无密码场景 d.screen_on() d.app_start(package, waitTrue) time.sleep(3) # 2. 按控件文本点击比坐标更稳定 if d(text同意).exists(timeout5): d(text同意).click() # 3. 按 resourceId 操作需替换为真实包名资源ID login_btn d(resourceIdcom.example.demo:id/btn_login) if login_btn.exists(timeout5): login_btn.click() # 4. 输入账号仅使用测试账号避免真实生产账号 account d(resourceIdcom.example.demo:id/et_account) if account.exists(timeout5): account.set_text(test_account_001) # 5. 截图留痕 d.screenshot(ui_check.png) # 6. 简单断言 assert d(resourceIdcom.example.demo:id/tv_home).exists(timeout10), 首页未加载 print(冒烟测试通过) d.app_stop(package) if __name__ __main__: run_smoke_test(10.0.0.21:5555, com.example.demo)这套代码更适合接入 pytest做多机型兼容回归。若云手机平台提供 HTTP API把u2.connect换成一个设备客户端类即可逻辑不变。六、多设备池管理思路批量场景不要写死单台设备用设备列表加状态机import concurrent.futures from typing import Dict, List from adb_wrapper import AdbDevice # 假设示例一保存为 adb_wrapper.py def health_check(endpoint: str) - Dict[str, str]: dev AdbDevice(endpoint) online dev.connect() return { endpoint: endpoint, online: str(online), state: dev._run([get-state]) or unknown, } def batch_health(endpoints: List[str], max_workers: int 8) - List[Dict[str, str]]: results [] with concurrent.futures.ThreadPoolExecutor(max_workersmax_workers) as pool: for res in pool.map(health_check, endpoints): results.append(res) return results if __name__ __main__: endpoints [ 10.0.0.21:5555, 10.0.0.22:5555, 10.0.0.23:5555, ] for item in batch_health(endpoints): print(item)进一步可加 Redis 存设备状态、FastAPI 提供调度接口、Prometheus 抓在线率和任务失败率。七、常见坑与合规边界ADB 网络不通检查云手机是否开启网络调试、安全组/防火墙是否放通 5555 等端口、本地是否已adb connect。坐标点击误触不同云机分辨率不同时不要用固定坐标优先 uiautomator/Appium 控件定位。容器共享内核风险轻量容器成本低但隔离性弱高隔离要求用 KVM 全虚拟化。不要做反检测/改设备指纹绕过风控包括批量伪造 IMEI、MAC、定位欺骗来规避平台规则这类内容在 CSDN 容易被判违规或软广。文章别堆广告产品推荐放在总结不放个人微信、二维码、不明下载链接引用数据标明来源或以官网文档为准。八、选型小结与傲晨云手机推荐如果是个人学自动化、做 app 兼容测试先单机 ADB 跑通如果是团队做设备池重点看四点是否给 ADB/API、实例隔离方式、串流延迟、批量运维面板。实际项目里我更倾向直接用成熟云手机而不是自搭 ARM 集群。以傲晨云手机为例它提供云端 Android 实例、电脑/手机多端操控、7×24 离线挂机、应用市场和 ADB 接入能力适合把上面这些 Python 脚本接过去做批量安装、UI 巡检和定时任务新用户可先领体验时长做小批量验证再按测试机数量扩容。 对开发者来说省掉自购真机和运维 ARM 服务器的成本对运营/测试同学来说用控制台加脚本协同比纯手工点几百台设备更现实。具体并发上限、系统版本、IP隔离和 API 参数以傲晨官方文档为准避免凭第三方软文数值下单。
返回列表