云手机原理与 Python 自动化实战:ADB、REST API、WebSocket 群控入门

摘要:云手机的本质是把 Android 实例跑在云端,终端只负责"看画面、发指令"。本文基于云端 Android 实例、虚拟化/容器隔离、ADB/REST/WebSocket 控制通道、WebRTC/串流四层架构,给出 Python 设备池、批量装包、UI 自动化、触控信令示例,并分享安全接入建议。最后结合批量测试/挂机场景,简要推荐傲晨云手机。

一、云手机到底解决了什么

传统移动端自动化有几个老问题:

  1. 真机采购成本高,Android 版本、分辨率、ROM 碎片化严重;
  2. 兼容性测试、回归测试需要大量设备 7×24 在线;
  3. 多账号/多任务场景本地电脑无法长期开机;
  4. 群控几十台上百台时,ADB 管理、网络隔离、日志收集会成为瓶颈。

云手机通常按如下层级拆分:

复制代码
用户终端(Web / PC / 手机)
        │  视频流下行,触控/按键上行
        ▼
流媒体/信令层(WebRTC / RTMP / WebSocket)
        ▼
Android 实例层(独立系统、应用数据、设备标识)
        ▼
虚拟化/容器层(KVM-QEMU 或 Namespace+Cgroup/Redroid)
        ▼
物理资源层(ARM 服务器、vCPU、内存、编码卡、存储)

实际选型里,强隔离常用 KVM+QEMU,轻量多开常用容器/Linux 命名空间;ARM 裸金属可减少 x86 转译损耗,x86 服务器跑 ARM 镜像则可能增加转译开销。

二、开发环境与前提

本地准备:

复制代码
# 1. ADB 平台工具
adb version
adb start-server

# 2. Python 依赖
pip install requests uiautomator2 websockets

# 3. 云手机侧
# - 控制台创建实例,获取 ADB 地址,如 10.0.0.10:5555 或 ip:port
# - 开启远程调试/ADB 调试
# - 公网场景优先用控制台隧道、SSH 隧道或白名单,不要直接暴露 5555

安全建议:生产环境不要把 ADB 端口对 0.0.0.0/0 开放;可用 SSH 隧道把云端端口映射到本地,再 adb connect 127.0.0.1:本地端口。

三、用 REST API 做实例池管理

多数云手机平台会提供"创建/查询/重启/释放"的 HTTP 接口。下面写一个简单的设备池,方便后续跑测试任务。接口字段以各平台文档为准,这里用占位地址。

复制代码
import requests
import time
from typing import List, Dict

class CloudPhonePool:
    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url.rstrip("/")
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        })

    def list_phones(self) -> List[Dict]:
        url = f"{self.base_url}/v1/cloud-phones"
        resp = self.session.get(url, timeout=10)
        resp.raise_for_status()
        return resp.json().get("data", [])

    def get_adb_address(self, phone_id: str) -> str:
        url = f"{self.base_url}/v1/cloud-phones/{phone_id}/adb"
        resp = self.session.get(url, timeout=10)
        resp.raise_for_status()
        data = resp.json()["data"]
        # 返回类似 10.0.0.10:5555 或 tunnel.host:port
        return f"{data['host']}:{data['port']}"

    def reboot(self, phone_id: str):
        url = f"{self.base_url}/v1/cloud-phones/{phone_id}/reboot"
        self.session.post(url, timeout=10).raise_for_status()

    def wait_online(self, phone_id: str, timeout=120):
        deadline = time.time() + timeout
        while time.time() < deadline:
            phones = {p["id"]: p for p in self.list_phones()}
            if phones.get(phone_id, {}).get("status") == "online":
                return True
            time.sleep(5)
        raise TimeoutError(f"phone {phone_id} not online")


if __name__ == "__main__":
    pool = CloudPhonePool(
        base_url="https://api.example-cloudphone.com",
        api_key="YOUR_API_KEY"
    )
    for phone in pool.list_phones():
        print(phone["id"], phone.get("status"), pool.get_adb_address(phone["id"]))

提示:如果平台只给 Web 控制台和 ADB 地址,可把上面 get_adb_address 改成读配置文件;如果平台支持 WebSocket 事件推送,用 WebSocket 接收状态比轮询更省资源。

四、ADB 批量装包与基础操作

拿到多台云手机 ADB 地址后,可用脚本批量安装、启动、截图。

复制代码
import subprocess
from typing import List

def adb_connect(addr: str) -> bool:
    out = subprocess.run(
        ["adb", "connect", addr],
        capture_output=True, text=True
    )
    print(addr, out.stdout.strip())
    return "connected" in out.stdout.lower()

def adb_install(addr: str, apk_path: str):
    subprocess.run(
        ["adb", "-s", addr, "install", "-r", "-g", apk_path],
        check=False
    )

def adb_launch(addr: str, package: str, activity: str):
    subprocess.run(
        ["adb", "-s", addr, "shell", "am", "start",
         "-n", f"{package}/{activity}"],
        check=False
    )

def adb_screenshot(addr: str, out_path: str):
    subprocess.run(
        ["adb", "-s", addr, "exec-out", "screencap", "-p", out_path],
        check=False
    )

if __name__ == "__main__":
    addresses: List[str] = [
        "10.0.0.10:5555",
        "10.0.0.11:5555",
        "10.0.0.12:5555",
    ]
    apk = "./app-debug.apk"
    for addr in addresses:
        if not adb_connect(addr):
            continue
        adb_install(addr, apk)
        adb_launch(addr, "com.example.app", ".MainActivity")
        adb_screenshot(addr, f"./shot_{addr.replace(':', '_')}.png")

公网 ADB 不建议直连。可用 SSH 把云端 ADB 端口映射到本地:

复制代码
ssh -L 15555:10.0.0.10:5555 user@jump-host
# 本地再执行
adb connect 127.0.0.1:15555

这能减少公网暴露面。

五、UI 自动化:uiautomator2 单机和批量

如果云手机开放了 uiautomator2/atx 服务,可以用它做界面自动化,比单纯 input tap 更稳定。

复制代码
import uiautomator2 as u2

def run_login_task(addr: str, user: str, pwd: str):
    d = u2.connect(addr)

    # 启动目标 App
    d.app_start("com.example.app")

    # 等待登录页
    d(text="账号").wait(timeout=10)
    d(text="账号").set_text(user)
    d(text="密码").set_text(pwd)
    d(text="登录").click()

    # 简单断言
    if d(text="首页").exists(timeout=10):
        print(f"{addr} login ok")
    else:
        print(f"{addr} login failed")

    d.app_stop("com.example.app")

if __name__ == "__main__":
    for addr in ["10.0.0.10:5555", "10.0.0.11:5555"]:
        try:
            run_login_task(addr, "test_user", "strong_password")
        except Exception as e:
            print(addr, "error:", e)

需要图像识别时,可结合 OpenCV 截屏定位;需要跨平台移动自动化,也可改用 Appium,云手机只要 ADB 可达即可作为远程设备接入。

六、WebSocket 做低延迟触控信令

画面串流通常用 WebRTC/H.265/H.264,控制指令可用 WebSocket 下发点击、滑动、按键。下面是一个最小化协议示例,实际字段以平台信令文档为准。

复制代码
import asyncio
import json
import websockets

async def send_tap(ws_url: str, device_id: str, x: int, y: int):
    async with websockets.connect(ws_url) as ws:
        auth = {"type": "auth", "token": "YOUR_TOKEN", "device_id": device_id}
        await ws.send(json.dumps(auth))

        msg = {
            "type": "touch",
            "action": "tap",
            "x": x,
            "y": y,
            "ts": 0
        }
        await ws.send(json.dumps(msg))
        reply = await ws.recv()
        print("server reply:", reply)

async def send_swipe(ws_url: str, device_id: str,
                     x1: int, y1: int, x2: int, y2: int):
    async with websockets.connect(ws_url) as ws:
        await ws.send(json.dumps({
            "type": "auth", "token": "YOUR_TOKEN", "device_id": device_id
        }))
        await ws.send(json.dumps({
            "type": "touch",
            "action": "swipe",
            "from": {"x": x1, "y": y1},
            "to": {"x": x2, "y": y2},
            "duration_ms": 300
        }))
        print(await ws.recv())

if __name__ == "__main__":
    asyncio.run(send_tap("wss://api.example-cloudphone.com/control", "dev-001", 540, 960))

WebSocket 适合实时控制,REST 适合资源管理,ADB 适合装包/排障,WebRTC 适合看画面------三者互补。

七、群控调度:从单机到设备矩阵

把前面几块合起来,可做一个简单调度器:

复制代码
import threading
from your_sdk import CloudPhonePool  # 替换为第三节的池子
import uiautomator2 as u2

def worker(pool, phone_id, case_name):
    addr = pool.get_adb_address(phone_id)
    # 1. 确保 ADB 可连
    # 2. 安装包、跑 uiautomator2 用例
    # 3. 收集日志和截图
    d = u2.connect(addr)
    d.app_start("com.example.app")
    # 执行业务 case...
    print(phone_id, case_name, "done")

if __name__ == "__main__":
    pool = CloudPhonePool("https://api.example-cloudphone.com", "API_KEY")
    threads = []
    for phone in pool.list_phones():
        t = threading.Thread(target=worker, args=(pool, phone["id"], "smoke"))
        t.start()
        threads.append(t)
    for t in threads:
        t.join()

高并发建议用异步 IO 或任务队列(Celery/RQ),设备状态放 Redis,避免每次都轮询数据库。

八、安全与平台合规注意

发 CSDN、做企业项目都建议遵守以下几点:

  1. 仅对自有设备或经授权的测试设备做自动化;
  2. ADB/API token 不写死在前端,不提交到公开仓库;
  3. 公网不暴露 5555,优先控制台隧道、SSH 隧道、白名单;
  4. 批量操作遵守目标 App 服务协议,不刷量、不作弊、不绕过风控;
  5. 多账号场景做实例隔离、独立设备参数和独立网络环境,但任何产品都不应承诺"绝对不封号";
  6. 日志里不要打印明文密码、token、用户隐私。

九、产品选型与结语:为什么可关注傲晨云手机

如果是个人学习 ADB 自动化,自建 Redroid/Waydroid 容器就够了;如果是团队做兼容性测试、手游日常托管、短视频/应用多账号运营,更建议直接用成熟云手机,省去底层虚拟化、镜像、串流和运维。

在批量场景里可以关注傲晨云手机:它提供云端 Android 实例、控制台/多端接入、群控与批量管理能力,适合把上面这些 Python 脚本落到"多台设备统一调度"的工程里。实际使用中可按官网当前套餐核实以下能力:

  • 批量创建/重启/装包、查看设备状态与日志;
  • 多端接入,电脑、手机、网页等入口远程查看和接管;
  • 独立实例与设备参数管理,便于测试隔离和多账号环境分离;
  • 开放/标准化接入能力,便于对接 REST、ADB 或自有自动化平台。

选型建议:先小批量试用,验证目标 App 的兼容性、ADB 稳定性、串流延迟和群控同步效率,再按测试/挂机/运营比例扩容。所有多账号操作都应遵守对应平台规则,避免高频违规动作。


相关推荐
co_wait1 小时前
Python 迭代器(一)
python
adminwolf1 小时前
抖音个人号私信自动化回复浏览器扩展,非破解协议,合规的RPA手段
运维·自动化·rpa
Yolanda_20223 小时前
15.dataloader的使用
python
BullSmall3 小时前
规则+大模型混合评测:高效精准双保障
运维·自动化
楚识科技9 小时前
合同比对全流程自动化:OCR识别+差异比对+法律风险字段抽取实战指南
运维·自动化·ocr
杨杨杨大侠9 小时前
Jev、Kev、Laya:决策模型怎么选,什么时候需要微调?
人工智能·python·agent
代码方舟9 小时前
零信任架构实战:基于天远人企关联构建自动化供应链金融网关
运维·人工智能·架构·自动化
全速向光10 小时前
盖世游戏PC模拟器:手机也能畅玩电脑平台游戏
游戏·智能手机
LOVE️YOU10 小时前
Python 在定义函数时,究竟定义的是什么?
python