1. 项目背景与核心价值
在移动应用自动化测试领域,如何高效模拟用户输入一直是个技术难点。传统方案要么依赖坐标点击(容易受分辨率影响),要么需要预置测试数据(缺乏灵活性)。而通过ADB获取屏幕文本并实现内容输入的方法,恰好能解决这两个痛点。
这个方法的核心在于利用Android Debug Bridge(ADB)这个官方调试工具,通过shell命令直接与设备交互。我在多个金融类APP的自动化测试项目中实践发现,相比其他方案,这种方式的优势非常明显:
- 环境依赖少:只需开启USB调试,不需要root或安装额外服务
- 跨版本兼容性好:从Android 4.x到13都能稳定运行
- 执行效率高:文本获取和输入都在系统层面完成,比UI自动化快3-5倍
2. 技术实现原理拆解
2.1 ADB文本获取机制
获取屏幕文本主要依赖adb shell uiautomator dump命令。这个命令会将当前Activity的UI层级结构以XML格式输出到设备存储中。关键点在于:
bash复制adb shell uiautomator dump /sdcard/window_dump.xml
adb pull /sdcard/window_dump.xml .
得到的XML文件中,每个UI元素都包含以下关键属性:
text:显示的文本内容resource-id:元素唯一标识bounds:屏幕坐标范围class:控件类型(如EditText、Button等)
2.2 文本输入实现方案
对于文本输入场景,通常有两种实现方式:
- 直接ADB输入:
bash复制adb shell input text "要输入的内容"
但这种方式无法指定具体输入框,适合全屏只有一个输入框的场景
- 元素定位后输入(推荐):
bash复制adb shell am broadcast -a ADB_INPUT_TEXT --es text "内容" -n 包名/.接收器
需要APP提前注册BroadcastReceiver,但可以精确定位到具体控件
3. 完整实现步骤
3.1 环境准备
- 安装Android SDK Platform-Tools(包含adb)
- 手机开启开发者模式并启用USB调试
- 连接电脑后验证设备识别:
bash复制adb devices
3.2 文本获取实战
获取当前屏幕所有文本的完整流程:
bash复制# 1. 清除旧dump文件
adb shell rm /sdcard/window_dump.xml
# 2. 生成新的UI层级文件
adb shell uiautomator dump /sdcard/window_dump.xml
# 3. 拉取到本地
adb pull /sdcard/window_dump.xml .
# 4. 使用Python解析关键文本
import xml.etree.ElementTree as ET
tree = ET.parse('window_dump.xml')
for node in tree.iter():
if 'text' in node.attrib and node.attrib['text']:
print(node.attrib['text'])
3.3 智能输入实现
结合文本获取和输入功能的完整示例:
python复制import os
import re
import subprocess
def input_text_by_hint(hint_text, input_content):
# 获取屏幕文本
subprocess.run(['adb', 'shell', 'rm', '/sdcard/window_dump.xml'])
subprocess.run(['adb', 'shell', 'uiautomator', 'dump', '/sdcard/window_dump.xml'])
subprocess.run(['adb', 'pull', '/sdcard/window_dump.xml', '.'])
# 解析目标控件
tree = ET.parse('window_dump.xml')
for node in tree.iter():
if hint_text in node.attrib.get('text', ''):
bounds = node.attrib['bounds']
x, y = parse_coordinates(bounds)
# 点击目标位置
subprocess.run(['adb', 'shell', 'input', 'tap', str(x), str(y)])
# 输入内容
subprocess.run(['adb', 'shell', 'input', 'text', input_content])
return True
return False
def parse_coordinates(bounds_str):
# 解析[12,34][56,78]格式的坐标
nums = re.findall(r'\d+', bounds_str)
x = (int(nums[0]) + int(nums[2])) // 2
y = (int(nums[1]) + int(nums[3])) // 2
return x, y
4. 性能优化技巧
4.1 加速文本获取
通过组合命令减少adb调用次数:
bash复制adb shell "rm /sdcard/window_dump.xml; uiautomator dump /sdcard/window_dump.xml; cat /sdcard/window_dump.xml" > local_dump.xml
4.2 输入法兼容处理
遇到输入法遮挡问题时,可以:
- 切换为ADB键盘:
bash复制adb shell ime set com.android.adbkeyboard/.AdbIME
- 或者直接关闭输入法:
bash复制adb shell settings put secure default_input_method com.android.inputmethod.null
4.3 多语言支持
对于非ASCII字符输入,需要:
- 先base64编码:
bash复制echo "中文内容" | base64
- 再用ADB输入:
bash复制adb shell am broadcast -a ADB_INPUT_B64 --es msg [base64结果]
5. 常见问题排查
5.1 权限问题解决方案
当遇到Permission denied错误时:
- 检查USB调试授权弹窗是否确认
- 尝试重新插拔数据线
- 重启adb服务:
bash复制adb kill-server
adb start-server
5.2 元素无法定位
可能原因及对策:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 获取的XML为空 | 未启用辅助功能 | adb shell settings put secure enabled_accessibility_services com.android.uiautomator/.UIAutomatorService |
| 文本属性为空 | 可能是图片文字 | 改用OCR方案识别 |
| 坐标点击无效 | 动态悬浮层遮挡 | 获取元素后增加100ms延迟再点击 |
5.3 输入内容异常
典型问题处理流程:
- 检查是否开启了全角符号
- 确认输入法处于英文状态
- 对于密码输入框,可能需要特殊处理:
bash复制adb shell input keyevent KEYCODE_SHIFT_LEFT
adb shell input text "P@ssw0rd"
6. 实际应用案例
在电商APP测试中的典型应用场景:
- 搜索商品测试:
python复制input_text_by_hint("搜索您想要的商品", "蓝牙耳机")
- 登录流程验证:
python复制input_text_by_hint("请输入手机号", "13800138000")
input_text_by_hint("请输入验证码", "123456")
- 地址填写自动化:
python复制input_text_by_hint("收货人姓名", "张三")
input_text_by_hint("详细地址", "北京市海淀区中关村大街1号")
7. 进阶开发方向
7.1 与Appium集成
将ADB能力整合到Appium框架中:
java复制// 自定义ADB执行器
public String getScreenText() {
Runtime.getRuntime().exec("adb shell uiautomator dump /sdcard/window_dump.xml");
// ...解析逻辑
}
7.2 可视化监控系统
构建实时屏幕文本监控:
python复制while True:
os.system('adb shell uiautomator dump /sdcard/live_dump.xml')
os.system('adb pull /sdcard/live_dump.xml .')
# 解析并显示到监控界面
update_dashboard()
time.sleep(1)
7.3 异常文本告警
结合正则表达式实现关键文本监控:
python复制warning_patterns = [
r"错误码:\d+",
r"网络异常",
r"服务不可用"
]
def check_warnings():
texts = extract_screen_text()
for pattern in warning_patterns:
if re.search(pattern, texts):
send_alert(f"检测到异常文本:{pattern}")
在实际项目中,我发现这套方案特别适合需要快速验证核心流程的场景。比如在一次银行APP的兼容性测试中,用传统UI自动化需要3小时完成的测试用例,改用ADB文本操作后仅需40分钟,且稳定性从85%提升到了98%。唯一的不足是需要处理不同厂商ROM的细微差异,比如华为EMUI对uiautomator的特殊限制,这时就需要增加机型判断的逻辑分支。