Skip to main content

Android UI 自动化的异步 Python 客户端

Project description

async-uiautomator2

面向 Android UI 自动化的异步 Python 客户端。

async-uiautomator2 不修改 u2.jar,也不重写 Android 端服务。它复用 uiautomator2 的设备端能力,在 Python 侧提供 async API、ADB socket HTTP/JSON-RPC 客户端、u2.jar 生命周期管理、typed selector 和 XPath 查询。

特性

  • 异步 API:公开入口使用 async / await,适合 FastAPI、FastStream、任务队列 worker 和长期运行的自动化服务。
  • 不阻塞事件循环:第一阶段通过 asyncio.to_thread() 包装同步 adbutils 调用,避免把 ADB shell、push、socket I/O 直接跑在事件循环主路径上。参考 Python 官方文档: asyncio.to_thread
  • ADB socket 直连 u2.jar:每次 HTTP 请求通过 ADB socket 连接设备端 u2.jar:9008, 手写最小 HTTP/1.1 请求,不依赖本地 adb forward 端口。
  • JSON-RPC 封装:提供 deviceInfoclickdumpWindowHierarchyobjInfowaitForExists 等常用 JSON-RPC 调用,并把常见错误映射到 uiautomator2.exceptions
  • u2.jar 生命周期管理:启动、ready 检查、停止和异常后的自动重启集中在 server 层; 并发重启使用锁和 generation 控制,避免多个协程同时拉起服务。
  • typed selector:使用 d.select(text="确定", resource_id="...") 代替 d(text="确定"),让 IDE 和类型检查器更早发现 selector 字段拼写错误。
  • 本地 XPath 匹配:通过 dumpWindowHierarchy 获取 XML,再复用 uiautomator2.xpath 的表达式能力做本地匹配,适合弹窗检测、临时定位和低频 watcher。
  • 多设备并发:不同设备可以在同一事件循环中自然并发;单设备内部仍建议按 UI 流程顺序执行。
  • 可测试 backendasync_connect(..., device_factory=...) 支持注入 fake ADB backend, 单元测试不需要真实 Android 设备。

安装

uv add async-uiautomator2

依赖:

  • Python 3.12+
  • uiautomator2>=3.5.2
  • adbutils,由 uiautomator2 间接提供

使用前请确保设备已连接并授权:

adb devices

快速开始

import asyncio
from async_uiautomator2 import async_connect


async def main():
    async with await async_connect("emulator-5554") as d:
        print(await d.info)
        await d.app_start("com.example")
        await d.click(100, 200)


asyncio.run(main())

不使用 async with 时,需要显式关闭当前客户端持有的 u2.jar stream:

d = await async_connect("emulator-5554")
try:
    await d.click(100, 200)
finally:
    await d.close()

设备 API

info = await d.info
xml = await d.dump_hierarchy()
output = await d.shell("getprop ro.product.model")

await d.click(100, 200)
await d.long_click(100, 200, duration=0.5)
await d.swipe(100, 800, 100, 200, duration=0.3)
await d.drag(100, 800, 100, 200, duration=0.5)
await d.send_keys("hello", clear=True)
await d.clear_text()
await d.push("local.txt", "/data/local/tmp/local.txt")
await d.app_start("com.example")
await d.app_stop("com.example")
await d.app_clear("com.example")

dump_hierarchy() 默认调用:

await d.dump_hierarchy(compressed=False, pretty=False, max_depth=50)

pretty=True 会使用 lxml 格式化 XML。

Typed Selector

推荐写法:

ok = d.select(
    text="确定",
    resource_id="com.example:id/ok",
    clickable=True,
)

if await ok.exists:
    await ok.click()

支持的常用能力:

await obj.info
await obj.exists
await obj.wait(timeout=10)
await obj.wait(exists=False, timeout=10)
await obj.click(timeout=10)
await obj.long_click(duration=0.5)
await obj.swipe("left", steps=10)
await obj.drag(100, 200, duration=0.5)
await obj.set_text("hello")
await obj.send_keys("hello")
await obj.clear_text()

child = obj.child(text="设置")
peer = obj.sibling(description="更多")

字段会从 Python 风格转换为 uiautomator2 原始字段:

Python 字段 uiautomator2 字段
text_contains textContains
text_matches textMatches
text_starts_with textStartsWith
class_name className
class_name_matches classNameMatches
description_contains descriptionContains
description_matches descriptionMatches
description_starts_with descriptionStartsWith
resource_id resourceId
resource_id_matches resourceIdMatches
package_name packageName
package_name_matches packageNameMatches
long_clickable longClickable

需要临时使用原始字段时,可以走低层逃生口:

await d.select_raw(textContains="确定").click()

本项目刻意不实现 d(text="OK") / d(**kwargs),避免把无类型提示的 selector 入口作为主线 API。

XPath

XPath 基于 XML dump 做本地匹配:

if await d.xpath("权限请求").exists:
    await d.xpath("允许").click()

常用方法:

selector = d.xpath("@com.example:id/ok")

await selector.exists
await selector.info
await selector.all()
await selector.get()
await selector.wait(timeout=10)
await selector.wait_gone(timeout=10)
await selector.get_text()
await selector.click()
await selector.click_exists()
selector.child("//android.widget.TextView")

支持 uiautomator2.xpath 的常用简写:

写法 含义
"确定" 匹配 text、content-desc 或 resource-id 等于该值
"@com.example:id/ok" 匹配 resource-id
"%确定%" text 或 content-desc 包含该值
"确定%" text 或 content-desc 前缀匹配
"%确定" text 或 content-desc 后缀匹配
"//android.widget.Button" 标准 XPath

u2.jar 获取方式

默认不依赖 experiment/ 目录,也不把 u2.jar 二进制文件打进 wheel。启动时按顺序解析:

  1. async_connect(..., jar_path="...") 显式传入的路径。
  2. 已安装包中的 assets/u2.jar 资源,例如 uiautomator2 自带资源。
  3. 本机缓存目录中的 u2-<version>.jar
  4. uiautomator2/assets/sync.sh 使用的 jar 源下载到缓存: https://public.uiauto.devsleep.com/u2jar/0.2.2/u2.jar

可用环境变量覆盖缓存目录:

$env:ASYNC_UIAUTOMATOR2_CACHE_DIR="D:\cache\async-uiautomator2"

也可以显式传入本地 jar:

d = await async_connect("emulator-5554", jar_path="D:/tools/u2.jar")

常驻服务示例

from async_uiautomator2 import async_connect

devices = {}


async def startup():
    devices["emulator-5554"] = await async_connect("emulator-5554")


async def click_permission(serial: str):
    d = devices[serial]
    if await d.xpath("权限请求").exists:
        await d.xpath("允许").click()


async def shutdown():
    for d in devices.values():
        await d.close()

并发语义

  • 多设备之间可以并发,例如 await asyncio.gather(run("device-a"), run("device-b"))
  • 同一设备上的服务启动、停止、重启由锁保护。
  • 同一设备的 UI 操作仍应按界面状态顺序执行;并发 watcher 适合低频弹窗检测,不适合同时乱点。
  • shell、push 等阻塞 ADB 调用已隔离到线程;第一阶段不保证取消后设备端命令立即停止。

当前范围

已覆盖:

  • async_connect()
  • AsyncDevice
  • AsyncUiObject
  • AsyncXPathSelector
  • ADB socket HTTP / JSON-RPC
  • u2.jar setup、ready、stop、restart
  • fake backend 单元测试

暂不覆盖:

  • 完整复刻 uiautomator2 全量 API
  • 修改或重写 Android 端 u2.jar
  • 纯异步 ADB transport
  • d(text="OK") / d(**kwargs) 兼容入口
  • 高频复杂 UI 轮询优化

开发

uv sync
uv run pytest -q
uv run python -m compileall -q src/async_uiautomator2

引用

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

async_uiautomator2-0.1.3.tar.gz (16.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

async_uiautomator2-0.1.3-py3-none-any.whl (22.2 kB view details)

Uploaded Python 3

File details

Details for the file async_uiautomator2-0.1.3.tar.gz.

File metadata

  • Download URL: async_uiautomator2-0.1.3.tar.gz
  • Upload date:
  • Size: 16.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for async_uiautomator2-0.1.3.tar.gz
Algorithm Hash digest
SHA256 77b3d2cc469fe57bd605f7b91ad4018e4aecc7d6793db0b58d3b970e4cc03654
MD5 b343a5ac9a54e7cccfe46a42931e4a78
BLAKE2b-256 a090adb215622d1d06f1f736e85dbb4019079f7bc97445f7cf8badc1741c0b98

See more details on using hashes here.

Provenance

The following attestation bundles were made for async_uiautomator2-0.1.3.tar.gz:

Publisher: publish.yml on jianjian2048/async-uiautomator2

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file async_uiautomator2-0.1.3-py3-none-any.whl.

File metadata

File hashes

Hashes for async_uiautomator2-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 b0c663259ed0abdfa7b3dcad2f54bbf79d2a5dae063381e57799a768688dbeab
MD5 07e83a8e1970c5a93d54fb6abb63222a
BLAKE2b-256 728f6ff961dcbea6f14dd6b443271a812ef7e7e13c65c352b027b203d06a77ad

See more details on using hashes here.

Provenance

The following attestation bundles were made for async_uiautomator2-0.1.3-py3-none-any.whl:

Publisher: publish.yml on jianjian2048/async-uiautomator2

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page