AutoGLMPhone03-adb模块

模块一:当前文件夹核心内容梳理
1.1 核心知识极简概括
- 统一设备连接管理:封装ADB连接逻辑,提供对USB/WiFi/远程设备的统一连接、断开和状态查询接口
- 设备交互抽象层:屏蔽底层ADB命令复杂性,提供直观的设备控制方法如点击、滑动、返回等操作
- 专用输入系统:基于ADB Keyboard实现可靠的文本输入机制,支持中文等复杂字符输入
- 视觉反馈采集:提供屏幕截图功能,支持敏感画面检测及fallback机制
- 模块化架构设计:各功能组件解耦合,便于独立维护和扩展
1.2 子知识扩展
统一设备连接管理
- 连接类型识别:自动区分USB、WiFi和远程连接类型,根据device_id格式判断连接方式
- 连接状态跟踪:实时监控设备连接状态,支持单设备或多设备环境下的精确识别
- TCP/IP调试支持:提供enable_tcpip方法启用无线调试,支持自定义端口配置
- 异常处理机制:内置超时控制和异常捕获,确保网络不稳定的环境下连接可靠性
- IP地址发现:自动探测设备在网络中的IP地址,简化无线连接配置过程
是 否 ADBConnection connect disconnect list_devices enable_tcpip 连接成功? 返回True 返回False 解析设备信息 构建DeviceInfo列表
设备交互抽象层
- 坐标操作封装:将tap、swipe等坐标相关操作封装为简单函数调用,隐藏ADB命令细节
- 应用生命周期管理:提供launch_app、get_current_app等方法控制应用启动和状态查询
- 延时策略配置:每种操作可配置延时时间,默认使用timing配置,支持动态调整
- 多设备支持:所有操作均支持device_id参数,适配多设备并行测试场景
- 系统按键模拟:封装常用系统按键如home、back等,提供一致的操作接口
用户代码 device.py ADB命令 tap(x, y) adb shell input tap x y 执行结果 None(带delay) 用户代码 device.py ADB命令
专用输入系统
- ADB Keyboard集成:依赖ADB Keyboard实现稳定文本输入,避免原生输入法干扰
- 输入法切换机制:自动检测并切换至ADB Keyboard,操作完成后恢复原输入法
- Base64编码传输:采用Base64编码传输文本,确保特殊字符正确输入
- 预热与清理机制:输入前后添加适当延时,确保输入法切换和文本输入稳定性
- 兼容性保障:提供clear_text方法清除输入框内容,配合type_text使用更可靠
否 是 type_text 检测当前输入法 是ADB Keyboard? 切换到ADB Keyboard 直接输入 Base64编码文本 广播ADB_INPUT_B64事件
视觉反馈采集
- 截图完整流程:远程截图→拉取至本地→编码为Base64,提供完整的图像数据流
- 敏感画面处理:检测截图失败场景(如支付界面),返回黑色占位图并标记敏感属性
- 内存优化策略:使用临时文件和BytesIO减少内存占用,及时清理临时资源
- 尺寸信息保留:不仅返回图像数据,同时保留宽高信息便于UI分析
- 异常容错机制:各类异常情况下均能返回有效Screenshot对象,保证流程不中断
是 否 get_screenshot 执行远程截图 拉取截图到本地 文件存在? 读取并编码图像 创建fallback截图 清理临时文件 返回Screenshot对象
模块化架构设计
- 功能职责分离:connection、device、input、screenshot各自专注特定领域功能
- 统一导出接口:通过__init__.py统一暴露所有公共接口,简化外部引用
- 依赖注入友好:各模块设计考虑了可测试性,便于mock和单元测试
- 配置中心化:通过timing配置集中管理各类操作延时,便于统一调整
- 扩展性预留:采用类和函数式混合设计,方便新增功能和定制化需求
init.py connection.py device.py input.py screenshot.py apps.py timing.py
1.3 知识点详细说明
统一设备连接管理
初始化流程
ADBConnection类是整个连接管理的核心,通过adb_path参数指定ADB可执行文件路径,默认为"adb"。初始化过程中不进行实际连接操作,仅保存配置参数。这种懒加载设计避免了不必要的连接尝试,提高初始化性能。
设备发现机制
list_devices方法通过执行adb devices -l命令获取所有连接设备信息,并解析输出构建DeviceInfo对象列表。解析过程中会根据device_id格式判断连接类型:
- 包含":"的为REMOTE类型(网络连接)
- 包含"emulator"的为USB类型(模拟器)
- 其余默认为USB类型(物理设备)
此外还会解析设备型号(model)等附加信息,为上层应用提供更多设备上下文。
连接管理策略
connect方法支持TCP/IP连接,会自动处理端口默认值(5555)并验证连接结果。支持超时控制,默认10秒超时防止长时间阻塞。
disconnect方法可以断开指定设备或所有设备连接,保持连接环境清洁。
无线调试支持
enable_tcpip方法实现在USB连接设备上启用TCP/IP调试功能。这是无线连接的前提条件,需要设备先通过USB连接并在设备上执行adb tcpip命令。
设备信息服务
提供了多种设备信息服务方法:
- get_device_info:获取指定设备详细信息
- is_connected:检查设备连接状态
- get_device_ip:获取设备IP地址
创建ADBConnection实例 connect()成功 disconnect()调用 reconnect enable_tcpip()成功 无线连接建立 disconnect Initialized Connected Disconnected TCPIP_Enabled Wireless_Connected 连接状态 get_device_ip() 运行应用 截图 Device_Listed IP_Detected App_Running Screenshot_Captured
设备交互抽象层
坐标操作实现
设备交互层主要封装了各类触摸和按键操作,这些操作通过调用ADB shell命令实现。例如tap方法实际执行的是adb shell input tap x y命令。
为了适应不同性能的设备和应用响应速度,几乎所有操作都支持delay参数,默认使用timing配置中的相应值。这种设计既保证了操作的可靠性,又提供了灵活的性能调优手段。
滑动操作优化
swipe方法除了基本的起点终点参数外,还支持duration_ms参数控制滑动持续时间。当未指定时,会根据滑动距离自动计算合适的持续时间,范围限定在1000-2000毫秒之间,确保滑动操作自然流畅。
应用管理机制
launch_app方法通过应用名称查找对应的包名,然后使用monkey命令启动应用。这种方法比直接使用am命令启动Activity更加稳定,因为它会自动寻找应用的Launcher Activity。
get_current_app通过解析dumpsys window输出识别当前前台应用,依赖apps.py中维护的应用名称与包名映射关系。
多设备环境支持
所有设备操作方法都支持device_id参数,通过_get_adb_prefix函数构建带设备标识的ADB命令前缀。这种设计使得同一套代码可以轻松适配单设备和多设备环境。
是 否 设备操作函数 device_id是否指定 adb -s {device_id} ... adb ... 执行ADB命令
专用输入系统
ADB Keyboard集成原理
type_text方法依赖ADB Keyboard实现文本输入,通过发送广播事件ADB_INPUT_B64传递Base64编码的文本内容。这种方式绕过了系统输入法,直接向应用发送文本,避免了输入法候选词、联想等功能的干扰。
输入法切换流程
detect_and_set_adb_keyboard方法首先获取当前默认输入法,如果不是ADB Keyboard则进行切换。切换后会执行一次空输入预热键盘,确保后续输入操作正常工作。
restore_keyboard用于恢复原来的输入法,通常在完成文本输入操作后调用,保持设备环境的一致性。
文本编码处理
为确保各种字符都能正确传输,文本在发送前会经过Base64编码。这解决了特殊字符、中文等在命令行传输中可能出现的编码问题。
时序控制
输入操作涉及多个步骤:切换输入法→输入文本→恢复输入法,每个步骤都需要适当的延时确保操作完成。配置项如keyboard_switch_delay、text_input_delay等控制各环节的时间间隔。
用户代码 input.py ADB Keyboard Android系统 detect_and_set_adb_keyboard() 查询当前输入法 返回输入法信息 设置为ADB Keyboard 激活ADB Keyboard 发送空文本预热 确认激活 type_text("测试文本") Base64编码 发送ADB_INPUT_B64广播 向焦点控件输入文本 restore_keyboard(original_ime) 恢复原始输入法 用户代码 input.py ADB Keyboard Android系统
视觉反馈采集
截图流程详解
get_screenshot方法通过三个步骤完成截图:
- 在设备上执行
screencap命令生成截图文件 - 将截图文件拉取到本地临时目录
- 读取本地文件并转换为Base64编码
敏感画面处理
Android系统对于某些敏感界面(如支付密码输入界面)会阻止截图操作。当截图失败时,会返回一个黑色占位图,并将is_sensitive标志设为True,提醒上层应用注意隐私保护。
资源管理策略
使用uuid生成唯一临时文件名避免冲突,操作完成后立即删除临时文件释放磁盘空间。使用BytesIO在内存中处理图像数据,避免频繁的磁盘IO操作。
数据结构设计
Screenshot数据类包含base64_data、width、height和is_sensitive四个字段,提供了完整的截图信息。其中is_sensitive字段特别重要,它标识了截图是否涉及敏感内容。
跨平台兼容性
通过PIL库处理图像,屏蔽了不同平台间图像格式的差异。Base64编码确保了数据在网络传输中的兼容性。
是 否 get_screenshot调用 远程截图 截图成功? 拉取截图文件 创建黑色占位图 设置is_sensitive=true 读取并编码图像 获取图像尺寸 清理临时文件 构造Screenshot对象
模块化架构设计
模块划分原则
整个adb模块按照功能划分为四个核心模块:
- connection.py: 负责设备连接管理
- device.py: 提供设备交互操作
- input.py: 处理文本输入相关功能
- screenshot.py: 实现屏幕截图功能
这种划分遵循单一职责原则,每个模块只关注特定领域的功能。
接口统一暴露
通过init.py文件统一导入并暴露所有公共接口,外部使用者只需引入phone_agent.adb即可访问全部功能,无需关心内部模块结构。
依赖管理
各模块间的依赖关系清晰,主要依赖关系包括:
- device.py依赖apps.py获取应用包名映射
- 多个模块依赖timing.py获取时序配置
- 所有模块都使用subprocess执行ADB命令
配置中心化
通过timing.py集中管理所有时序相关配置,支持环境变量覆盖,便于根据不同设备性能和网络状况调整操作延时。
可测试性设计
各模块函数设计考虑了可测试性,支持device_id参数便于在多设备环境中进行精确控制,也为mock测试提供了便利。
外部使用者 init.py connection.py device.py input.py screenshot.py apps.py timing.py subprocess
模块二:核心代码逻辑
2.1 核心类/方法速查表
| 类/方法名 | 定位(文件:行号) | 输入输出 | 使用场景示例(1句话) | 调试提示(如:断点打在哪) |
|---|---|---|---|---|
| ADBConnection | connection.py:47 | adb_path → ADB连接管理器实例 | 初始化连接管理器以控制多个设备 | 在__init__方法确认adb_path是否正确 |
| connect | connection.py:57 | address, timeout → (bool, str) | 连接到远程设备以进行无线控制 | 在命令执行前检查address格式 |
| list_devices | connection.py:128 | 无 → DeviceInfo列表 | 获取所有连接设备列表以选择目标设备 | 在解析line处检查输出格式 |
| tap | device.py:48 | x, y, device_id, delay → None | 在屏幕上指定坐标点击按钮或元素 | 在subprocess.run调用处确认命令 |
| swipe | device.py:117 | start_x, start_y, end_x, end_y, duration_ms, device_id, delay → None | 在屏幕上滑动以滚动列表或切换页面 | 在duration_ms计算处验证逻辑 |
| type_text | input.py:12 | text, device_id → None | 在输入框中输入用户名或密码 | 在encoded_text处检查编码结果 |
| get_screenshot | screenshot.py:33 | device_id, timeout → Screenshot对象 | 获取屏幕截图用于视觉分析或验证操作结果 | 在temp_path生成处确认路径有效性 |
2.2 最小复现示例(伪代码)
python
# ①依赖注入
import subprocess
from dataclasses import dataclass
from typing import Optional
# 模拟配置
class MockTimingConfig:
default_tap_delay = 1.0
TIMING_CONFIG = MockTimingConfig()
# ②关键调用
def _get_adb_prefix(device_id: Optional[str]) -> list:
"""获取ADB命令前缀"""
if device_id:
return ["adb", "-s", device_id]
return ["adb"]
def tap(x: int, y: int, device_id: str = None):
"""点击屏幕指定位置"""
# 关键调用:执行ADB命令
adb_prefix = _get_adb_prefix(device_id)
subprocess.run(
adb_prefix + ["shell", "input", "tap", str(x), str(y)],
capture_output=True
)
# 延时等待操作生效
import time
time.sleep(TIMING_CONFIG.default_tap_delay)
def get_screenshot(device_id: str = None) -> dict:
"""获取屏幕截图"""
import tempfile
import os
import uuid
from PIL import Image
import base64
from io import BytesIO
# 创建临时文件路径
temp_path = os.path.join(tempfile.gettempdir(), f"screenshot_{uuid.uuid4()}.png")
adb_prefix = _get_adb_prefix(device_id)
# 执行截图命令
subprocess.run(
adb_prefix + ["shell", "screencap", "-p", "/sdcard/tmp.png"],
capture_output=True
)
# 拉取截图文件
subprocess.run(
adb_prefix + ["pull", "/sdcard/tmp.png", temp_path],
capture_output=True
)
# 读取并编码图像
img = Image.open(temp_path)
width, height = img.size
buffered = BytesIO()
img.save(buffered, format="PNG")
base64_data = base64.b64encode(buffered.getvalue()).decode("utf-8")
# 清理临时文件
os.remove(temp_path)
# 返回Screenshot对象信息
return {
"base64_data": base64_data,
"width": width,
"height": height,
"is_sensitive": False
}
# ③断言验证
if __name__ == "__main__":
# 模拟点击操作
tap(500, 500) # 点击屏幕中心
# 获取截图验证操作结果
screenshot = get_screenshot()
# 验证截图数据完整性
assert "base64_data" in screenshot
assert screenshot["width"] > 0
assert screenshot["height"] > 0
assert isinstance(screenshot["base64_data"], str)
print("✅ ADB操作模块核心功能验证通过")