搜索

教程

Python 控制安卓模拟器:ADB 自动化教程

面向 Macro Automation Studio 的 Python 控制安卓模拟器教程:在 BlueStacks 上点击、滑动、输入、按键和截图,adb 由应用替你管理。

  • Windows
  • Mac
  • 模拟器
  • 云手机
  • 手机
  • Python SDK
进阶 更新于 阅读约 8 分钟
本页目录
  1. 开始之前
  2. 各部分如何配合
  3. 创建项目
  4. 屏幕尺寸与坐标
  5. 用 Python 通过 adb 截图
  6. 点击
  7. 滑动
  8. 输入文字
  9. 按键
  10. 打开并检查应用
  11. 一个完整的脚本
  12. adb 在这篇 Python 教程里的位置
  13. 可能出什么问题
  14. Could not discover RPC port
  15. 点击落在错误的位置
  16. input_text 什么都没输入
  17. 找不到设备或设备离线

大多数 Python 控制安卓模拟器的教程最后都变成一个包着 adb shell input tapsubprocess 封装。这篇面向 Macro Automation Studio(MAS)的 Python 控制安卓模拟器教程走另一条路:应用掌管 adb,你的脚本通过 mas 包和应用对话。指南从一个新项目走到一个打开应用、搜索关键词、滚动并保存截图的脚本。适合想驱动 BlueStacks、LDPlayer、MuMu Player 或 MEmu 又不想写 adb 调用的 Python 开发者。

开始之前

  • 已安装 MAS 并用试用或套餐登录。参见安装 MAS
  • 一个模拟器正在运行、已开启 ADB,并已在设备组中添加为设备。参见设备
  • 你懂基础 Python。其他什么都不用你装:MAS 自带 Python 3.13 环境,里面有 mas

各部分如何配合

MAS 以 python -u -m src.app 启动你的脚本,工作目录是项目文件夹。它通过环境变量(MAS_RPC_PORTMAS_RPC_HOSTMAS_DEVICE_IDMAS_SESSION_TOKEN)传入连接信息。每个 mas.* 调用都变成一个发给应用的 JSON-RPC 2.0 请求。应用执行 adb 命令、模板搜索或 OCR,然后把结果送回来。设备在你的第一行代码运行之前就已绑定,所以没有 connect() 调用,也没有序列号要管理。参见核心概念

创建项目

  1. 打开,点击新建项目
  2. 选择代码型,把目标设备设为移动端,给项目命名,点击创建项目
  3. 打开项目。Code Editor 会显示 src/app.py
  4. 在设备选择器中选择模拟器,想试某段代码时点击运行F5)。停止Shift+F5

屏幕尺寸与坐标

你传给 SDK 的每个坐标都是相对设备屏幕左上角的像素偏移,以设备自己的分辨率计。模拟器窗口在你显示器上的大小无关紧要。

python
import mas

size = mas.get_screen_size()
print(f"Screen: {size.width}x{size.height}")

center = (size.width // 2, size.height // 2)
mas.click(*center)

get_screen_size() 返回一个带 widthheightScreenSizeget_device_info() 返回同样的信息,外加设备的 nametypeconnected 标志。当脚本必须适应分辨率变化时,像结尾的示例那样按尺寸的比例计算位置。模板图片和 OCR 区域适应不了分辨率变化;参见图像识别指南

用 Python 通过 adb 截图

python
import base64
import mas

shot = mas.take_screenshot()
print(shot.width, shot.height, shot.timestamp)

with open("screen.png", "wb") as f:
    f.write(base64.b64decode(shot.base64))

take_screenshot() 返回一个 Screenshotbase64 存放 PNG,widthheight 是它的像素尺寸,timestamp 是 ISO 8601 字符串。上面的文件会落在项目文件夹里,因为那是工作目录。把同一个对象用 screenshot=shot 传给多个 find_object 调用,它们就会搜索同一帧,而不是再次截图。

点击

python
mas.click(270, 800)                 # tap, then wait 1000 ms
mas.click(270, 800, delay_ms=300)   # shorter pause for tight loops

click(x, y, delay_ms=1000) 点击一次,然后等待 delay_ms 让应用响应。点击没有按住时长;长按请用带 duration_mskey_press

滑动

python
mas.swipe((270, 750), (270, 300), duration_ms=400)    # scroll a list down: drag from lower to upper
mas.swipe((100, 500), (400, 500), duration_ms=1000)   # drag and drop: slow and deliberate

swipe(from_coords, to_coords, duration_ms=1000) 接受两个 (x, y) 元组。时长短是轻扫,时长长是拖拽。捏合手势是单独的调用:zoom_in()zoom_out() 默认以屏幕中心为准,percent=50

输入文字

python
mas.click(270, 120, delay_ms=500)          # focus the field first
mas.input_text("hello world")
mas.input_text("new value", clear=True)    # replace what is there

input_text(text, delay_ms=0, clear=False) 向获得焦点的输入框输入,所以先点一下输入框。clear=True 会把光标移到末尾并删除已有字符,然后再输入。

按键

python
from mas import KeyCode

mas.key_press(KeyCode.BACK)
mas.key_press(KeyCode.HOME)
mas.key_press(KeyCode.ENTER)
mas.key_press(KeyCode.DELETE, repeat=10)          # ten presses in one command
mas.key_press(KeyCode.POWER, duration_ms=3000)    # long press

key_press(key_code, modifiers=None, duration_ms=100, repeat=1) 发送一个安卓按键事件。duration_ms 达到 500 或以上就成为长按;超出的具体毫秒数不会被精确遵守。repeat 的范围是 1 到 100,比 Python 循环快得多。KeyCode 还有方向键、音量键、媒体键和数字键。

打开并检查应用

python
mas.open_app("com.android.settings", timeout_ms=5000)
print(mas.get_current_app())                        # package name in front
print(mas.is_app_focused("com.android.settings"))   # True or False

if mas.get_app_state("com.android.chrome") == mas.NOT_RUNNING:
    mas.open_app("com.android.chrome")

mas.close_app("com.android.settings")

open_app(package_name, timeout_ms=2000) 按包名启动并等待 timeout_ms。要知道包名,手动打开应用然后打印 mas.get_current_app()get_app_state 返回 NOT_INSTALLEDNOT_RUNNINGRUNNING_IN_BACKGROUND_SUSPENDEDRUNNING_IN_BACKGROUNDRUNNING_IN_FOREGROUNDclose_app 强制停止该包。

一个完整的脚本

这个脚本打开安卓设置、搜索一个关键词、滚动结果、保存截图,并用 OCR 确认关键词在屏幕上。搜索框是你在 Asset Lab 中裁剪的模板;把 ID 换成你的。滑动使用屏幕尺寸的比例,所以在任何分辨率下都能工作。

python
import base64
import sys
import time

import mas
from mas import KeyCode

PACKAGE = "com.android.settings"
TERM = "Display"

images = mas.images({"search_box": 301})


def main():
    size = mas.get_screen_size()
    mas.log(f"Screen {size.width}x{size.height}")

    mas.open_app(PACKAGE, timeout_ms=5000)
    if not mas.is_app_focused(PACKAGE):
        mas.log(f"{PACKAGE} did not come to the front", level="error")
        sys.exit(2)

    box = mas.find_object_retry(images.search_box, total_tries=3, time_sleep=2.0)
    if box is None:
        mas.log("Search box not found; crop it in Asset Lab", level="error")
        sys.exit(2)
    mas.click(box.x, box.y, delay_ms=800)
    mas.input_text(TERM, clear=True)
    mas.key_press(KeyCode.ENTER)
    time.sleep(2)

    x = size.width // 2
    mas.swipe((x, int(size.height * 0.75)), (x, int(size.height * 0.35)), duration_ms=600)
    time.sleep(1)

    shot = mas.take_screenshot()
    with open("search_results.png", "wb") as f:
        f.write(base64.b64decode(shot.base64))
    mas.log(f"Saved search_results.png ({shot.width}x{shot.height})")

    text = mas.read_text(screenshot=shot, psm=11)
    mas.log(f"Term on screen: {TERM.lower() in text.text.lower()}")

    mas.key_press(KeyCode.HOME)


if __name__ == "__main__":
    main()

psm=11read_text 读取整帧上的零散文字,适合结果列表。OCR 指南介绍读取单个计数器用的区域和模式。如果你更愿意描述任务而不是写代码,MAS Agent 可以替你写这类脚本。

adb 在这篇 Python 教程里的位置

你从不调用 adb,但底层干活的正是它。

  • 可执行文件。 安装包自带 adb。在 Windows 上位于 C:\ProgramData\MacroAutomationStudio\3rdparty,还有一份内置副本作为后备。在 Mac 上位于应用包内,Homebrew 的 adb 作为后备。不会往你的 PATH 里放任何东西。安装页有细节。
  • 连接。 模拟器在一个本地 TCP 端口上暴露 adb。你点击启动运行时,MAS 对设备卡片上的端口运行 adb connect 127.0.0.1:<port>,并在运行期间保持这个会话。
  • 输入。 click 变成 adb shell input tapswipe 变成 adb shell input touchscreen swipeinput_text 变成 adb shell input textkey_press 变成 adb shell input keyevent。捏合手势直接写入模拟器的触摸输入设备。
  • 屏幕。 take_screenshot 和每个 find_object 都用 adb exec-out screencap -p 截图,这是二进制安全的形式,在 Windows 上不会损坏 PNG。get_screen_size 读取 wm sizeget_current_app 读取窗口管理器。
  • 应用。 open_app 通过 monkey 触发启动器 intent,失败时回退到 am startclose_app 运行 am force-stop
  • 服务器。 MAS 运行自己的 adb 服务器。如果你 PATH 上的第二个 adb 版本启动了自己的服务器,两者会互相替换,你的设备会掉回连接中。ADB 故障排查页按端口列出了修复方法。

因为这一切都由应用完成,在 BlueStacks 上写的脚本无需修改就能在 LDPlayer、云设备或通过本地端口连接的你自己的手机上运行。

可能出什么问题

Could not discover RPC port

脚本是从终端启动的,而不是从 MAS,所以环境变量缺失。用 Code Editor 里的运行或从设备卡片运行它。

点击落在错误的位置

你用的是窗口像素而不是设备像素,或者模拟器的分辨率变了。打印 mas.get_screen_size(),和你传入的坐标对比。Asset Lab 给出的坐标已经是设备像素。

input_text 什么都没输入

没有输入框获得焦点。用 click 点一下输入框,并给它 delay_ms=500 再输入。有些应用会弹出盖住输入框的键盘;输入后用 key_press(KeyCode.BACK) 关掉它。

找不到设备或设备离线

模拟器中 ADB 是关闭的、设备卡片上的端口错了,或者另一个 adb 服务器接管了。按照 ADB 故障排查处理。

如果你驱动的应用是游戏,请像对待任何其他自动化账号一样谨慎。没有任何自动化工具是 100% 无风险的,请负责任地使用自动化,并自行斟酌决定。

下一步

相关页面

这个页面有帮助吗?

有疑问? 在 Discord 提问