PiaoKe___头像
关注
云手机原理与 Python 自动化实战:ADB 连接、批量调度与 CI 集成封面图

云手机原理与 Python 自动化实战:ADB 连接、批量调度与 CI 集成

摘要:云手机把 Android 实例跑在云端,开发者可通过 ADB/开放接口做远程调试、自动化测试、批量运维。本文从架构原理讲起,给出单机 ADB 脚本、多设备批量框架、CI 冒烟测试示例,并总结连接超时、截图失败、批量并发等常见问题。最后结合团队运维场景,轻量推荐傲晨云手机的接入方式。

一、为什么用云手机

传统移动端测试/运维常遇到三类问题:

  • 真机采购成本高:机型、系统版本、分辨率越多,买机越贵。
  • 本地电脑跑模拟器吃资源:x86 转译、GPU 渲染、多开后 CPU 占用高。
  • 无人值守困难:下班后、断网后本地设备不能持续执行任务。

云手机的本质是把 Android 系统部署在云端服务器(通常为 ARM 实例或受控虚拟化环境),通过网络把画面和操作回传。对开发者来说,它暴露的入口主要是:

  1. 控制台:创建/重启/销毁实例、查看状态。
  2. ADB 远程连接:adb connect ip:port 后像本地设备一样调试。
  3. 开放 API/SDK:用代码批量创建设备、执行命令、拉取日志。
  4. 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

云手机侧:

  1. 控制台创建 Android 实例并进入“运行中”。
  2. 开启 ADB/远程调试,拿到 ip:port(不同厂商可能是直连地址、SSH 隧道或带连接码)。
  3. 本机测试连通性:
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

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--