简介
uiautomator2 是比 ADB 更强大的 Android 自动化框架,支持元素定位、控件操作、应用管理等高级功能。本教程适合需要更精细控制的开发者。
一、环境准备
1.1 前置要求
- Python 3.6 或更高版本
- Android 手机(需开启开发者模式和 USB 调试)
- USB 数据线
- 已安装 ADB 工具(参考第一篇教程)
1.2 检查 Python 环境
bash
python --version
# 应显示 Python 3.6 或更高版本
1.3 检查 ADB 连接
bash
adb devices
# 应显示已连接的设备
二、安装 uiautomator2
2.1 安装 Python 库
bash
pip install uiautomator2
使用国内镜像加速:
bash
pip install uiautomator2 -i https://pypi.tuna.tsinghua.edu.cn/simple
2.2 初始化手机环境
重要步骤: 首次使用需要在手机上安装 ATX-Agent 和 uiautomator 服务。
bash
# 方法一:使用命令行工具初始化
python -m uiautomator2 init
# 方法二:使用 Python 脚本初始化
python
>>> import uiautomator2 as u2
>>> u2.connect_usb() # 会自动安装必要组件
>>> exit()
初始化过程:
- 会在手机上安装两个 APK:
ATXAgent.apk- 核心服务app-uiautomator.apk或app-uiautomator-test.apk- UI 自动化服务
- 安装过程需要 1-3 分钟
- 手机上会弹出安装提示,点击"安装"
2.3 验证安装
创建测试文件 test_u2.py:
python
import uiautomator2 as u2
# 连接设备
d = u2.connect() # 自动连接 USB 设备
print("设备信息:", d.info)
print("✅ uiautomator2 安装成功!")
运行测试:
bash
python test_u2.py
成功会显示设备信息(型号、分辨率、Android 版本等)。
三、uiautomator2 核心功能
3.1 设备连接方式
python
import uiautomator2 as u2
# 方式1:自动连接(推荐)
d = u2.connect()
# 方式2:通过 USB 连接
d = u2.connect_usb()
# 方式3:通过设备序列号连接
d = u2.connect('device_serial')
# 方式4:通过 WiFi 连接(需先配置)
d = u2.connect('192.168.1.100')
3.2 基础操作
python
# 获取设备信息
print(d.info)
# 获取屏幕分辨率
width, height = d.window_size()
print(f"分辨率: {width}x{height}")
# 截图
d.screenshot("screen.png")
# 点击坐标
d.click(500, 1000)
# 滑动
d.swipe(500, 1500, 500, 500, 0.3) # 从下往上滑
# 长按
d.long_click(500, 1000)
# 输入文本
d.send_keys("Hello World")
# 按键操作
d.press("home") # 返回主屏幕
d.press("back") # 返回键
3.3 应用管理
python
# 启动应用
d.app_start("com.ss.android.ugc.aweme")
# 停止应用
d.app_stop("com.ss.android.ugc.aweme")
# 清除应用数据
d.app_clear("com.ss.android.ugc.aweme")
# 检查应用是否运行
if d.app_current().get('package') == "com.ss.android.ugc.aweme":
print("抖音正在运行")
# 获取当前应用信息
current = d.app_current()
print(f"当前应用: {current['package']}")
四、编写自动化脚本
4.1 基础滑动脚本
创建 u2_basic_swipe.py:
python
"""
uiautomator2 基础滑动脚本
功能:自动滑动视频
"""
import uiautomator2 as u2
import time
def main():
# 连接设备
d = u2.connect()
print(f"✅ 已连接设备: {d.info['productName']}")
# 获取屏幕尺寸
width, height = d.window_size()
print(f"📱 屏幕分辨率: {width}x{height}")
# 配置参数
interval = 5 # 滑动间隔(秒)
count = 0
print(f"⏰ 每 {interval} 秒滑动一次")
print("按 Ctrl+C 停止\n")
try:
while True:
count += 1
# 从屏幕下方滑到上方
# 使用相对坐标,自适应不同分辨率
d.swipe(
width // 2, # x: 屏幕中央
height * 0.8, # y1: 屏幕 80% 位置
width // 2, # x: 保持在中央
height * 0.2, # y2: 屏幕 20% 位置
0.3 # 滑动持续时间(秒)
)
print(f"[{count}] {time.strftime('%H:%M:%S')} ✅ 滑动成功")
time.sleep(interval)
except KeyboardInterrupt:
print(f"\n⏹️ 已停止,共执行 {count} 次滑动")
except Exception as e:
print(f"\n❌ 发生错误: {e}")
if __name__ == "__main__":
main()
运行:
bash
python u2_basic_swipe.py
4.2 高级功能脚本
创建 u2_advanced_swipe.py:
python
"""
uiautomator2 高级自动化脚本
功能:
1. 启动指定应用
2. 智能检测页面状态
3. 支持多种滑动模式
4. 异常处理和自动恢复
"""
import uiautomator2 as u2
import time
import random
from datetime import datetime
class VideoAutomation:
def __init__(self, package_name, interval=5, total_count=None):
"""
初始化视频自动化控制器
参数:
package_name: 应用包名
interval: 滑动间隔(秒)
total_count: 总滑动次数(None=无限次)
"""
self.package_name = package_name
self.interval = interval
self.total_count = total_count
self.device = None
self.count = 0
self.width = 0
self.height = 0
def connect(self):
"""连接设备"""
try:
self.device = u2.connect()
self.width, self.height = self.device.window_size()
print(f"✅ 已连接设备: {self.device.info.get('productName', 'Unknown')}")
print(f"📱 分辨率: {self.width}x{self.height}")
print(f"🤖 Android 版本: {self.device.info.get('version', 'Unknown')}")
return True
except Exception as e:
print(f"❌ 连接设备失败: {e}")
return False
def start_app(self):
"""启动应用"""
try:
print(f"🚀 正在启动应用: {self.package_name}")
self.device.app_start(self.package_name)
time.sleep(3) # 等待应用启动
# 验证应用是否启动成功
current = self.device.app_current()
if current.get('package') == self.package_name:
print(f"✅ 应用启动成功")
return True
else:
print(f"⚠️ 启动的应用不匹配,当前: {current.get('package')}")
return False
except Exception as e:
print(f"❌ 启动应用失败: {e}")
return False
def check_app_running(self):
"""检查应用是否在前台运行"""
try:
current = self.device.app_current()
return current.get('package') == self.package_name
except:
return False
def swipe_up(self):
"""向上滑动(下一个视频)"""
try:
# 使用屏幕中央位置滑动
x = self.width // 2
y1 = int(self.height * 0.75) # 起始位置:75%
y2 = int(self.height * 0.25) # 结束位置:25%
self.device.swipe(x, y1, x, y2, 0.3)
self.count += 1
return True
except Exception as e:
print(f"❌ 滑动失败: {e}")
return False
def swipe_down(self):
"""向下滑动(上一个视频)"""
try:
x = self.width // 2
y1 = int(self.height * 0.25)
y2 = int(self.height * 0.75)
self.device.swipe(x, y1, x, y2, 0.3)
return True
except Exception as e:
print(f"❌ 滑动失败: {e}")
return False
def random_swipe(self):
"""随机滑动(模拟人类行为)"""
# 90% 概率向上,10% 概率向下
if random.random() < 0.9:
return self.swipe_up()
else:
print(" ↓ 随机向下滑动")
return self.swipe_down()
def should_continue(self):
"""判断是否继续执行"""
if self.total_count is None:
return True
return self.count < self.total_count
def get_random_interval(self):
"""获取随机间隔时间(模拟人类)"""
# 在设定间隔基础上 ±20% 浮动
variation = self.interval * 0.2
return self.interval + random.uniform(-variation, variation)
def run(self):
"""运行自动化任务"""
if not self.connect():
return
if not self.start_app():
return
print(f"\n⏰ 平均滑动间隔: {self.interval} 秒(随机波动)")
if self.total_count:
print(f"🎯 目标次数: {self.total_count} 次")
else:
print(f"🔄 持续运行(按 Ctrl+C 停止)")
print("-" * 50)
start_time = time.time()
try:
while self.should_continue():
# 检查应用是否还在前台
if not self.check_app_running():
print("⚠️ 应用不在前台,尝试重新启动...")
if not self.start_app():
break
# 执行滑动
current_time = datetime.now().strftime('%H:%M:%S')
print(f"[{self.count + 1}] {current_time} ", end="")
if self.random_swipe():
print("✅")
else:
print("❌ 失败,继续...")
# 等待随机间隔
if self.should_continue():
wait_time = self.get_random_interval()
time.sleep(wait_time)
except KeyboardInterrupt:
print("\n\n⏹️ 用户停止")
except Exception as e:
print(f"\n❌ 发生错误: {e}")
finally:
elapsed = time.time() - start_time
self.print_statistics(elapsed)
def print_statistics(self, elapsed_time):
"""打印统计信息"""
print(f"\n{'='*50}")
print(f"📊 运行统计:")
print(f" - 总滑动次数: {self.count}")
print(f" - 运行时长: {int(elapsed_time)} 秒 ({int(elapsed_time/60)} 分钟)")
if self.count > 0:
print(f" - 平均间隔: {elapsed_time/self.count:.1f} 秒")
print(f"{'='*50}")
def main():
import argparse
# 解析命令行参数
parser = argparse.ArgumentParser(description='uiautomator2 视频自动化脚本')
parser.add_argument('package', help='应用包名,如: com.ss.android.ugc.aweme')
parser.add_argument('-i', '--interval', type=float, default=5.0,
help='滑动间隔(秒),默认5秒')
parser.add_argument('-n', '--count', type=int, default=None,
help='滑动次数,不指定则无限循环')
args = parser.parse_args()
# 创建并运行自动化任务
automation = VideoAutomation(
package_name=args.package,
interval=args.interval,
total_count=args.count
)
automation.run()
if __name__ == "__main__":
main()
4.3 智能元素定位脚本
创建 u2_smart_control.py:
python
"""
基于 UI 元素的智能控制脚本
功能:通过识别页面元素来智能操作
"""
import uiautomator2 as u2
import time
class SmartController:
def __init__(self, package_name):
self.package_name = package_name
self.device = u2.connect()
def start(self):
"""启动应用"""
self.device.app_start(self.package_name)
time.sleep(2)
def wait_and_click(self, text=None, description=None, timeout=10):
"""等待元素出现并点击"""
try:
if text:
# 通过文本查找并点击
self.device(text=text).click(timeout=timeout)
print(f"✅ 点击了: {text}")
return True
elif description:
# 通过描述查找并点击
self.device(description=description).click(timeout=timeout)
print(f"✅ 点击了: {description}")
return True
except u2.exceptions.UiObjectNotFoundError:
print(f"⚠️ 未找到元素")
return False
def swipe_if_video_exists(self):
"""如果检测到视频元素就滑动"""
# 示例:检测特定 ID 的视频容器
if self.device(resourceId="video_container").exists:
width, height = self.device.window_size()
self.device.swipe(width//2, height*0.8, width//2, height*0.2, 0.3)
print("✅ 检测到视频,已滑动")
return True
return False
def auto_handle_popups(self):
"""自动处理弹窗"""
# 常见弹窗按钮文本
close_texts = ["关闭", "取消", "不感兴趣", "跳过", "我知道了"]
for text in close_texts:
if self.device(text=text).exists:
self.device(text=text).click()
print(f"✅ 关闭弹窗: {text}")
time.sleep(0.5)
return True
return False
# 使用示例
if __name__ == "__main__":
controller = SmartController("com.ss.android.ugc.aweme")
controller.start()
# 自动处理启动页弹窗
time.sleep(2)
controller.auto_handle_popups()
# 持续滑动
for i in range(10):
controller.swipe_if_video_exists()
time.sleep(5)
五、运行指南
5.1 基础脚本运行
bash
# 运行基础滑动脚本
python u2_basic_swipe.py
5.2 高级脚本运行
抖音自动刷视频(每5秒一次):
bash
python u2_advanced_swipe.py com.ss.android.ugc.aweme
快手自动刷视频(每8秒一次,共50次):
bash
python u2_advanced_swipe.py com.smile.gifmaker -i 8 -n 50
B站自动刷视频(每3秒一次):
bash
python u2_advanced_swipe.py tv.danmaku.bili -i 3
5.3 查看帮助
bash
python u2_advanced_swipe.py -h
六、WiFi 无线连接配置
6.1 配置步骤
1. 确保手机和电脑在同一 WiFi 网络
2. 通过 USB 启用 WiFi 调试:
bash
# 查看手机 IP 地址
adb shell ip addr show wlan0
# 启用 WiFi ADB(端口 5555)
adb tcpip 5555
# 记下手机 IP 地址(如 192.168.1.100)
3. 连接 WiFi:
bash
# 断开 USB 线
adb connect 192.168.1.100:5555
4. 在脚本中使用 WiFi 连接:
python
import uiautomator2 as u2
# 使用 IP 地址连接
d = u2.connect('192.168.1.100')
6.2 WiFi 连接优势
- 无需 USB 线,更自由
- 可以同时控制多台手机
- 适合长时间运
行
七、UI 元素定位技巧
7.1 查看页面元素
安装 weditor(UI 查看器):
bash
pip install weditor
启动 weditor:
bash
python -m weditor
浏览器会自动打开 http://localhost:17310
使用方法:
- 输入设备 IP 或序列号,点击 Connect
- 点击 Dump Hierarchy 获取当前页面结构
- 点击元素查看属性(text、resourceId、className 等)
- 可以实时测试定位表达式
7.2 常用定位方法
python
import uiautomator2 as u2
d = u2.connect()
# 通过文本定位
d(text="立即登录").click()
# 通过文本包含
d(textContains="登录").click()
# 通过 resourceId 定位
d(resourceId="com.app:id/button").click()
# 通过 className 定位
d(className="android.widget.Button").click()
# 通过 description 定位
d(description="确认按钮").click()
# 组合定位
d(className="android.widget.Button", text="确认").click()
# 索引定位(多个相同元素时)
d(className="android.widget.Button")[0].click() # 第一个按钮
d(className="android.widget.Button")[1].click() # 第二个按钮
7.3 等待元素出现
python
# 等待元素出现(最多等待10秒)
d(text="加载完成").wait(timeout=10)
# 等待元素消失
d(text="加载中...").wait_gone(timeout=10)
# 判断元素是否存在
if d(text="登录").exists:
print("找到登录按钮")
# 获取元素信息
info = d(text="登录").info
print(info)
八、高级功能示例
8.1 自动点赞脚本
python
"""
视频自动点赞脚本
"""
import uiautomator2 as u2
import time
d = u2.connect()
d.app_start("com.ss.android.ugc.aweme")
time.sleep(3)
for i in range(10):
# 双击屏幕点赞(适用于抖音)
width, height = d.window_size()
d.double_click(width//2, height//2)
print(f"❤️ 点赞第 {i+1} 次")
time.sleep(1)
# 滑动到下一个视频
d.swipe(width//2, height*0.8, width//2, height*0.2, 0.3)
time.sleep(5)
8.2 自动关注脚本
python
"""
自动关注作者脚本
"""
import uiautomator2 as u2
import time
d = u2.connect()
d.app_start("com.ss.android.ugc.aweme")
time.sleep(3)
for i in range(5):
# 查找并点击关注按钮
if d(text="关注").exists:
d(text="关注").click()
print(f"✅ 关注第 {i+1} 个作者")
elif d(textContains="关注").exists:
d(textContains="关注").click()
print(f"✅ 关注第 {i+1} 个作者")
else:
print("⚠️ 未找到关注按钮")
time.sleep(2)
# 滑到下一个视频
width, height = d.window_size()
d.swipe(width//2, height*0.8, width//2, height*0.2, 0.3)
time.sleep(5)
8.3 截图并保存
python
"""
定时截图脚本
"""
import uiautomator2 as u2
import time
from datetime import datetime
d = u2.connect()
d.app_start("com.ss.android.ugc.aweme")
for i in range(10):
# 生成带时间戳的文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"screenshot_{timestamp}.png"
# 截图
d.screenshot(filename)
print(f"📸 已保存截图: {filename}")
# 滑动
width, height = d.window_size()
d.swipe(width//2, height*0.8, width//2, height*0.2, 0.3)
time.sleep(5)
8.4 监控屏幕变化
python
"""
监控屏幕变化脚本
"""
import uiautomator2 as u2
import time
d = u2.connect()
# 获取初始截图
last_screen = d.screenshot()
while True:
time.sleep(1)
current_screen = d.screenshot()
# 简单对比(可用图像相似度算法优化)
if current_screen != last_screen:
print("🔄 屏幕内容已变化")
last_screen = current_screen
九、故障排查
9.1 "ConnectionError: Unable to connect to device"
解决方案:
bash
# 1. 检查 ADB 连接
adb devices
# 2. 重新初始化 uiautomator2
python -m uiautomator2 init
# 3. 检查 ATX-Agent 是否运行
adb shell ps | grep atx
# 4. 重启 ATX-Agent
adb shell am force-stop com.github.uiautomator
python -m uiautomator2 init
9.2 元素定位失败
解决方案:
- 使用 weditor 查看实际元素属性
- 检查元素是否在屏幕可见区域
- 增加等待时间
- 使用更精确的定位方式
python
# 添加等待
d(text="按钮").wait(timeout=10)
# 尝试不同定位方式
if not d(text="登录").exists:
d(textContains="登录").click()
9.3 "UiObjectNotFoundError"
解决方案:
python
# 使用异常处理
try:
d(text="按钮").click()
except u2.exceptions.UiObjectNotFoundError:
print("元素不存在,跳过")
# 或先判断
if d(text="按钮").exists:
d(text="按钮").click()
9.4 操作响应慢
解决方案:
python
# 设置操作超时时间
d.settings['operation_delay'] = (0, 0) # 点击前后延迟
d.settings['operation_delay_methods'] = ['click', 'swipe']
# 设置 implicitly_wait
d.implicitly_wait(10) # 隐式等待10秒
9.5 WiFi 连接断开
解决方案:
bash
# 重新连接
adb connect <手机IP>:5555
# 保持连接
adb connect <手机IP>:5555
adb -s <手机IP>:5555 shell settings put global stay_on_while_plugged_in 7
十、性能优化
10.1 加快操作速度
python
# 关闭等待时间
d.settings['wait_timeout'] = 10 # 全局等待超时
d.settings['operation_delay'] = (0, 0) # 操作延迟
# 使用 shell 命令代替高级 API
d.shell("input swipe 500 1500 500 500 100") # 更快
10.2 减少资源占用
python
# 不需要截图时关闭
# 使用 shell 命令代替截图检查
# 批量操作
for i in range(100):
d.swipe(500, 1500, 500, 500, 0.1) # 减少滑动时间
10.3 异步操作
python
import asyncio
import uiautomator2 as u2
async def swipe_task(device, count):
for i in range(count):
width, height = device.window_size()
device.swipe(width//2, height*0.8, width//2, height*0.2, 0.3)
await asyncio.sleep(3)
async def main():
d = u2.connect()
await swipe_task(d, 10)
asyncio.run(main())
十一、完整项目结构
phone-automation-u2/
├── test_u2.py # 测试连接
├── u2_basic_swipe.py # 基础滑动
├── u2_advanced_swipe.py # 高级滑动
├── u2_smart_control.py # 智能控制
├── screenshots/ # 截图目录
│ └── screenshot_*.png
└── logs/ # 日志目录
└── automation.log
十二、uiautomator2 vs ADB 对比
| 功能 | uiautomator2 | ADB |
|---|---|---|
| 元素定位 | ✅ 支持 | ❌ 不支持 |
| 应用管理 | ✅ 便捷 | ⚠️ 需命令 |
| WiFi 连接 | ✅ 简单 | ⚠️ 需配置 |
| 学习曲线 | ⚠️ 中等 | ✅ 简单 |
| 性能 | ⚠️ 中等 | ✅ 快速 |
| 功能丰富度 | ✅ 丰富 | ⚠️ 基础 |
| 配置复杂度 | ⚠️ 需初始化 | ✅ 简单 |
十三、实战案例
13.1 完整的抖音自动刷视频脚本
python
"""
抖音自动刷视频完整脚本
功能:
- 自动启动抖音
- 智能跳过广告
- 随机间隔滑动
- 异常自动恢复
"""
import uiautomator2 as u2
import time
import random
from datetime import datetime
class DouyinAutomation:
def __init__(self):
self.device = u2.connect()
self.package = "com.ss.android.ugc.aweme"
self.count = 0
def start(self):
"""启动抖音"""
print("🚀 启动抖音...")
self.device.app_start(self.package)
time.sleep(3)
self.handle_popups()
def handle_popups(self):
"""处理启动弹窗"""
close_buttons = ["关闭", "跳过", "我知道了", "取消"]
for btn in close_buttons:
if self.device(text=btn).exists:
self.device(text=btn).click()
print(f"✅ 关闭弹窗: {btn}")
time.sleep(0.5)
def swipe_next(self):
"""滑到下一个视频"""
width, height = self.device.window_size()
# 添加轻微随机偏移
x = width // 2 + random.randint(-50, 50)
y1 = int(height * 0.75) + random.randint(-20, 20)
y2 = int(height * 0.25) + random.randint(-20, 20)
duration = random.uniform(0.25, 0.35)
self.device.swipe(x, y1, x, y2, duration)
self.count += 1
def check_status(self):
"""检查应用状态"""
current = self.device.app_current()
if current.get('package') != self.package:
print("⚠️ 应用不在前台,重新启动")
self.start()
return False
return True
def run(self, duration_minutes=30):
"""运行指定时长"""
self.start()
end_time = time.time() + duration_minutes * 60
print(f"🎬 开始刷视频,持续 {duration_minutes} 分钟")
print("-" * 50)
try:
while time.time() < end_time:
if not self.check_status():
continue
# 滑动
timestamp = datetime.now().strftime('%H:%M:%S')
print(f"[{self.count + 1}] {timestamp} ✅")
self.swipe_next()
# 随机间隔 4-8 秒
interval = random.uniform(4, 8)
time.sleep(interval)
except KeyboardInterrupt:
print("\n⏹️ 用户停止")
finally:
print(f"\n📊 总共观看 {self.count} 个视频")
if __name__ == "__main__":
automation = DouyinAutomation()
automation.run(duration_minutes=10) # 运行10分钟
十四、总结
恭喜完成 uiautomator2 教程!你现在已经掌握:
✅ uiautomator2 环境搭建和初始化
✅ 设备连接(USB 和 WiFi)
✅ 基础操作(点击、滑动、输入等)
✅ UI 元素定位技巧
✅ 应用管理和控制
✅ 高级功能实现
✅ 异常处理和优化
uiautomator2 优势:
- 功能强大,支持复杂交互
- 可以精确定位 UI 元素
- 支持无线连接
- Python API 友好
适用场景:
- 需要元素定位的自动化
- 复杂交互逻辑
- 应用测试
- UI 自动化操作
下一步建议:
- 学习图像识别(OpenCV)
- 研究 OCR 文字识别
- 探索 AI 辅助自动化
- 学习 Appium 实现跨平台自动化
参考资源:
- uiautomator2 文档:https://github.com/openatx/uiautomator2
- weditor 工具:https://github.com/alibaba/web-editor
- Android UI 文档:https://developer.android.com/reference/android/support/test/uiautomator/package-summary