
1. 项目概述与核心价值最近在折腾一个手机应用的自动化测试项目传统的Appium方案虽然成熟但启动慢、环境依赖重对于需要快速验证或者高频次执行的场景总感觉有点“杀鸡用牛刀”。后来我把目光投向了Scrcpy和ADB命令的组合。你可能知道Scrcpy是个开源的手机投屏工具延迟极低画质清晰但很多人可能没意识到当它和ADB命令结合起来能形成一个非常轻量、高效的自动化测试新思路。这个方案的核心就是利用Scrcpy实现实时、低延迟的手机屏幕监控同时通过Python调用ADB命令实现精准的屏幕坐标模拟点击、滑动等操作。它特别适合那些对执行速度有要求、或者环境搭建希望尽可能简单的测试场景比如冒烟测试、核心功能回归、甚至是简单的UI遍历。简单来说这个思路就是把“眼睛”Scrcpy实时画面和“手”ADB命令分开但又通过Python脚本紧密协同。你不再需要启动一个庞大的Appium Server也不需要处理复杂的WebDriver协议直接用最底层的ADB指令去操控设备响应速度非常快。对于测试开发工程师或者有一定Python基础的测试同学来说这是一个能极大提升效率、降低复杂度的实用技巧。接下来我就把这个方案的完整设计、核心实现细节以及我踩过的坑毫无保留地分享给你。2. 技术栈选型与设计思路拆解2.1 为什么是Scrcpy ADB Python在决定采用这个方案之前我对比过几种主流的移动端自动化方案。Appium功能全面生态好但需要启动Appium Server依赖Node.js环境执行速度相对较慢更适合复杂、跨平台的E2E测试。而像Airtest这类基于图像识别的框架虽然对新手友好但图像匹配本身有性能开销且对屏幕分辨率、颜色变化比较敏感。ScrcpyADBPython的组合优势在于“极致的轻量与直接”。Scrcpy它本身只是一个投屏工具基于高效的H.264视频流传输。它的价值在于为我们提供了一个近乎实时的手机屏幕“视频流”。我们可以通过获取其窗口句柄或者直接解析其传输的帧数据虽然复杂些来实时“看到”手机屏幕。对于自动化测试实时性意味着我们能立刻知道上一步操作的结果判断界面是否跳转成功元素是否出现。ADB (Android Debug Bridge)这是Android SDK提供的官方调试工具。adb shell input系列命令是操控手机的“金手指”可以直接模拟几乎所有的物理交互点击 (tap)、滑动 (swipe)、文本输入 (text)、物理按键 (keyevent)。它的执行是立刻生效的几乎没有延迟因为这是系统级别的指令。Python作为胶水语言Python在这里扮演“大脑”和“协调者”的角色。它负责启动和管理Scrcpy进程通过subprocess模块调用ADB命令解析Scrcpy的屏幕信息来计算坐标并组织整个测试逻辑流。Python丰富的库如opencv-python用于更高级的图像识别pynput或pyautogui用于在Scrcpy窗口上模拟点击作为备选方案也让整个方案的扩展性很强。这个组合的设计思路是“观察-决策-执行”的循环。Python脚本持续从Scrcpy“观察”屏幕状态根据预设的逻辑或图像识别结果“决策”下一步操作然后通过ADB“执行”对应的点击或滑动命令。它剥离了重型测试框架的中间层直连设备核心接口因此速度飞快资源占用小。2.2 核心组件与工作流程整个方案可以拆解为以下几个核心组件它们协同工作的流程如下图所示概念描述设备连接与ADB环境确保手机通过USB或网络连接到电脑且ADB命令可用。这是所有操作的基础。Scrcpy投屏服务在后台启动Scrcpy将手机屏幕投射到电脑上的一个窗口。我们可以选择让这个窗口可见用于人工监控或不可见纯后台获取画面数据需要额外处理。屏幕信息获取模块这是关键。我们需要获取当前投屏窗口的实时图像并建立手机屏幕坐标与投屏窗口坐标或图像像素坐标之间的映射关系。因为ADB命令操作的坐标是基于手机屏幕物理分辨率的而我们在电脑上看到的是经过缩放的窗口。ADB命令执行模块Python通过subprocess调用adb shell input等命令将计算好的坐标或指令发送给手机。测试逻辑控制器Python主脚本这是大脑它包含测试用例控制着整个“观察-决策-执行”的循环。例如它可能先截图然后寻找“登录”按钮的图案找到后计算其中心坐标最后发送adb shell input tap x y命令。注意这里有一个常见的误解。我们并非直接点击Scrcpy的窗口来操控手机虽然可以但那属于桌面自动化范畴不稳定。我们是通过分析Scrcpy窗口的画面计算出目标在真实手机屏幕上的坐标然后让ADB去点击那个坐标。这是两种完全不同的技术路径后者更稳定、更底层。3. 环境搭建与核心工具详解3.1 ADB的安装与配置ADB是整个方案的基石。如果你的电脑还没有ADB安装步骤如下下载Android SDK Platform-Tools这是最纯净的方式。去Android开发者官网找到“Command line tools only”进行下载解压后里面就包含adb可执行文件。或者直接搜索“Platform-Tools”下载独立包。配置系统环境变量将解压后存放adb.exeWindows或adbMac/Linux的目录路径添加到系统的PATH环境变量中。验证安装打开命令行终端CMD、PowerShell或Terminal输入adb version。如果能看到版本号信息说明配置成功。接下来是连接手机开启手机的“开发者选项”。通常在“关于手机”里连续点击“版本号”7次。在“开发者选项”中开启“USB调试”。用USB数据线连接手机和电脑。此时手机会弹出“是否允许USB调试”的授权对话框勾选“始终允许”并确认。在电脑终端输入adb devices。如果看到设备列表中出现你的设备序列号且状态为device则表示连接成功。如果状态是unauthorized检查手机上的授权提示。实操心得推荐使用USB连接比无线ADB连接更稳定延迟更低。如果必须用无线先用USB执行adb tcpip 5555再adb connect 手机IP:5555。另外有些手机品牌如华为、小米可能需要额外在开发者选项里打开“USB调试安全设置”或安装特定的手机助手驱动才能被ADB识别这点需要留意。3.2 Scrcpy的安装与基本使用Scrcpy的安装非常简单下载前往Scrcpy的GitHub发布页面下载对应你操作系统的安装包如.exe, .dmg, 或AppImage。安装/解压Windows下直接运行安装程序Mac可能需拖动到应用程序文件夹Linux解压即可。基本使用连接手机后直接双击运行Scrcpy手机屏幕应该就会投射到电脑窗口上。你可以用鼠标在窗口里点击、拖动来操作手机这是Scrcpy自带的基本映射功能但我们自动化不用这种方式。Scrcpy有一些非常实用的启动参数对我们的自动化场景有帮助--no-control 启动Scrcpy但不接收电脑的输入鼠标键盘仅投屏。这样能防止手动误操作干扰自动化脚本。--bit-rate 2M 设置视频码率降低码率可以节省CPU和带宽但画质会下降。对于自动化清晰识别按钮即可可以设低些。--max-size 1024 将手机屏幕分辨率限制为1024宽度等比例缩放。降低分辨率可以进一步提升性能。--window-title ‘自动化测试监控’ 自定义投屏窗口的标题方便我们用Python脚本找到这个特定窗口。例如一个适合自动化后台运行的命令可能是scrcpy --no-control --bit-rate 2M --max-size 800 --window-title AutoTestMonitor3.3 Python环境与必要库你需要一个Python环境建议3.7以上。然后通过pip安装以下库opencv-python 用于图像处理和识别如果需要基于图像查找元素。pillow 另一个常用的图像处理库有时比OpenCV更轻便。pyautogui 可以用于获取屏幕截图、获取窗口位置作为获取Scrcpy窗口画面的一种备选方案并非必须我们有其他方法。numpy OpenCV的依赖也是处理图像数组的基础。安装命令pip install opencv-python pillow pyautogui numpy4. 核心实现屏幕坐标映射与ADB操控这是整个方案的技术核心理解了这里就掌握了精髓。4.1 获取Scrcpy窗口画面与坐标映射原理我们需要从Scrcpy投屏窗口实时“抓取”画面并知道画面上的一个点对应手机屏幕上的哪个坐标。方法一通过窗口句柄截图推荐更稳定这种方法不依赖Scrcpy的特殊接口通用性强。我们利用Python的pyautogui或win32guiWindows等库先找到Scrcpy窗口然后对其进行截图。import pyautogui import cv2 import numpy as np def find_and_capture_scrcpy(window_titleAutoTestMonitor): 通过窗口标题查找Scrcpy窗口并截图。 注意此方法要求Scrcpy窗口在屏幕前台且未被最小化。 # 获取所有窗口信息找到目标窗口 # 注意pyautogui的getWindowsWithTitle在某些环境下可能不准这里用简化描述。 # 实际生产代码可能需要使用win32gui (Windows) 或 Xlib (Linux) 来精确获取窗口句柄和位置。 # 假设我们已经获得了窗口的左上角坐标(x, y)和宽高(width, height) window_x, window_y, window_width, window_height 100, 100, 800, 450 # 示例值实际需动态获取 # 截取整个屏幕 full_screenshot pyautogui.screenshot() # 转换为OpenCV格式 full_screenshot_cv cv2.cvtColor(np.array(full_screenshot), cv2.COLOR_RGB2BGR) # 裁剪出目标窗口区域 window_image full_screenshot_cv[window_y:window_ywindow_height, window_x:window_xwindow_width] return window_image, (window_x, window_y, window_width, window_height) # 获取映射关系的关键我们需要知道手机的真实分辨率。 # 通过ADB命令获取adb shell wm size import subprocess def get_phone_resolution(): result subprocess.run([adb, shell, wm, size], capture_outputTrue, textTrue, encodingutf-8) output result.stdout.strip() # 输出格式通常为Physical size: 1080x2340 或 1080x2340 if Physical size: in output: resolution_str output.split(:)[1].strip() else: resolution_str output width, height map(int, resolution_str.split(x)) return width, height phone_width, phone_height get_phone_resolution() window_img, (win_x, win_y, win_w, win_h) find_and_capture_scrcpy() # 坐标映射函数将窗口内的一个像素点坐标转换为手机屏幕坐标。 def window_point_to_phone_point(win_point_x, win_point_y): win_point_x, win_point_y: 在Scrcpy窗口图像内的坐标以窗口左上角为原点。 # 计算在窗口内的比例位置 ratio_x win_point_x / win_w ratio_y win_point_y / win_h # 映射到手机屏幕 phone_x int(phone_width * ratio_x) phone_y int(phone_height * ratio_y) return phone_x, phone_y原理Scrcpy默认会将手机屏幕等比例缩放以适应窗口或按指定max-size缩放。只要窗口内容没有被裁剪黑边是等比例缩放的结果那么窗口图像上的相对位置比例与手机屏幕上的相对位置比例就是一致的。因此我们通过adb shell wm size获取手机物理分辨率然后根据目标点在窗口图像上的坐标比例就能换算出在手机上的绝对坐标。重要注意事项如果Scrcpy启动时加了--crop参数裁剪了屏幕或者窗口被手动拉伸变形这个比例映射关系就会被破坏。因此为了自动化稳定建议固定Scrcpy的启动参数如--max-size并确保脚本运行时窗口大小和比例不变。方法二从Scrcpy直接获取视频帧高级Scrcpy本身是通过Socket传输H.264视频流的。理论上我们可以绕过GUI窗口直接连接到这个视频流并解码帧。这种方法更高效不依赖屏幕截图但实现复杂需要解析Scrcpy的通信协议和解码视频流。对于大多数自动化场景方法一已经足够可靠和简单。4.2 ADB命令模拟用户操作详解获取到手机屏幕坐标后就可以用ADB命令进行操控了。adb shell input是核心命令集。模拟点击 (Tap):adb shell input tap x yx和y就是我们上一步计算出的手机屏幕坐标。例如adb shell input tap 500 1200模拟滑动 (Swipe):adb shell swipe x1 y1 x2 y2 [duration(ms)]从点(x1, y1)滑动到点(x2, y2)。可选的duration参数表示滑动过程的耗时毫秒模拟慢速滑动。例如adb shell swipe 500 1600 500 800 500表示从中间偏下向上滑动耗时500毫秒。模拟按键 (Keyevent):adb shell input keyevent keycode模拟物理按键如HOME键、返回键、电源键等。常用键值3: HOME4: BACK返回24: VOLUME_UP音量加25: VOLUME_DOWN音量减26: POWER电源键66: ENTER回车67: DEL删除 例如adb shell input keyevent 4模拟按下返回键。输入文本 (Text):adb shell input text hello world直接输入文本相当于在光标处打字。注意不能输入中文和特殊字符如空格在早期版本需用%s代替现在一般直接支持空格。在Python中我们使用subprocess模块来调用这些命令import subprocess def adb_tap(x, y): 执行点击操作 subprocess.run([adb, shell, input, tap, str(x), str(y)], checkTrue) def adb_swipe(x1, y1, x2, y2, durationNone): 执行滑动操作 cmd [adb, shell, input, swipe, str(x1), str(y1), str(x2), str(y2)] if duration: cmd.append(str(duration)) subprocess.run(cmd, checkTrue) def adb_keyevent(keycode): 执行按键操作 subprocess.run([adb, shell, input, keyevent, str(keycode)], checkTrue) def adb_text(text): 输入文本 # 需要对文本进行简单转义确保shell命令正确 subprocess.run([adb, shell, input, text, text], checkTrue)4.3 结合图像识别定位元素进阶单纯依赖固定坐标的点击非常脆弱屏幕分辨率一变或者UI稍微改动脚本就失效了。因此在实际项目中我们通常需要图像识别来动态定位元素。我们可以用OpenCV的模板匹配功能。思路是事先截取好需要点击的按钮图标作为模板在实时获取的Scrcpy窗口画面中搜索这个模板找到匹配度最高的位置计算其中心坐标再映射为手机坐标进行点击。import cv2 def find_template_on_screen(screen_image, template_path, threshold0.8): 在屏幕图像中查找模板。 :param screen_image: 屏幕截图OpenCV格式BGR或灰度。 :param template_path: 模板图片路径。 :param threshold: 匹配度阈值高于此值认为匹配成功。 :return: (found, center_x, center_y) found为是否找到center为在screen_image中的坐标。 # 读取模板 template cv2.imread(template_path, cv2.IMREAD_GRAYSCALE) if template is None: raise FileNotFoundError(f模板图片未找到: {template_path}) # 将屏幕图像转为灰度 gray_screen cv2.cvtColor(screen_image, cv2.COLOR_BGR2GRAY) # 进行模板匹配 result cv2.matchTemplate(gray_screen, template, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc cv2.minMaxLoc(result) # TM_CCOEFF_NORMED方法下最大值越接近1匹配度越高 if max_val threshold: # 计算模板中心点在屏幕图像中的坐标 h, w template.shape top_left max_loc center_x top_left[0] w // 2 center_y top_left[1] h // 2 return True, center_x, center_y else: return False, 0, 0 # 在自动化脚本中使用 window_img, window_info find_and_capture_scrcpy() found, btn_center_x, btn_center_y find_template_on_screen(window_img, template_login_button.png) if found: # 将窗口坐标转换为手机坐标 phone_x, phone_y window_point_to_phone_point(btn_center_x, btn_center_y) # 执行点击 adb_tap(phone_x, phone_y) print(f已点击登录按钮手机坐标: ({phone_x}, {phone_y})) else: print(未找到登录按钮)实操心得模板匹配对图像的亮度、旋转、缩放比较敏感。为了提高鲁棒性可以使用多个模板不同状态下的按钮。对屏幕图像进行预处理如灰度化、直方图均衡化。考虑使用更高级的特征匹配方法如SIFT或ORBOpenCV内置但它们计算量更大。最实用的方法是确保测试环境手机型号、分辨率、系统主题相对固定并截取高质量的模板图片。5. 完整自动化测试脚本架构与实战现在我们把所有模块组合起来构建一个完整的、可复用的自动化测试脚本框架。5.1 脚本框架设计一个健壮的脚本框架应该包含以下部分配置管理存放设备信息、Scrcpy参数、模板图片路径、坐标配置等。设备控制层封装ADB命令操作点击、滑动等和屏幕截图/获取函数。元素识别层封装图像识别逻辑返回找到的元素坐标手机坐标。业务流程层将具体的测试用例编写成一个个函数调用设备控制和元素识别层。日志与报告记录操作步骤、成功失败信息并生成简单报告。异常处理与等待处理元素未找到、操作超时等异常并实现智能等待如等待某个元素出现。下面是一个简化的框架示例# config.py PHONE_RESOLUTION (1080, 2340) # 你的手机分辨率 SCRCPY_WINDOW_TITLE AutoTestMonitor TEMPLATES_DIR ./templates/ LOG_FILE ./automation.log # device_controller.py import subprocess import time from config import PHONE_RESOLUTION # ... 导入之前定义的 adb_tap, adb_swipe, get_phone_resolution, find_and_capture_scrcpy, window_point_to_phone_point 等函数 ... class DeviceController: def __init__(self): self.phone_width, self.phone_height PHONE_RESOLUTION # 可以在这里启动Scrcpy进程 self.scrcpy_process None self.start_scrcpy() def start_scrcpy(self): 启动Scrcpy投屏 import subprocess scrcpy_cmd [scrcpy, --no-control, --bit-rate, 2M, --max-size, 800, --window-title, SCRCPY_WINDOW_TITLE] self.scrcpy_process subprocess.Popen(scrcpy_cmd) time.sleep(3) # 等待Scrcpy启动稳定 def stop_scrcpy(self): 停止Scrcpy投屏 if self.scrcpy_process: self.scrcpy_process.terminate() self.scrcpy_process.wait() def get_screen_and_map_info(self): 获取当前屏幕和映射信息 window_img, (win_x, win_y, win_w, win_h) find_and_capture_scrcpy(SCRCPY_WINDOW_TITLE) return window_img, win_w, win_h def tap_on_phone(self, phone_x, phone_y): adb_tap(phone_x, phone_y) def swipe_on_phone(self, start_x, start_y, end_x, end_y, duration300): adb_swipe(start_x, start_y, end_x, end_y, duration) # ... 其他设备操作封装 ... # element_finder.py import cv2 from config import TEMPLATES_DIR # ... 导入之前定义的 find_template_on_screen 函数 ... class ElementFinder: def __init__(self, device_controller): self.dc device_controller def find_element(self, template_name, threshold0.85, timeout10, interval1): 在超时时间内循环查找元素。 :param template_name: 模板文件名不含路径。 :param threshold: 匹配阈值。 :param timeout: 超时时间秒。 :param interval: 每次查找间隔秒。 :return: (found, phone_x, phone_y) template_path f{TEMPLATES_DIR}{template_name} start_time time.time() while time.time() - start_time timeout: window_img, win_w, win_h self.dc.get_screen_and_map_info() found, center_x, center_y find_template_on_screen(window_img, template_path, threshold) if found: phone_x, phone_y window_point_to_phone_point(center_x, center_y, win_w, win_h, self.dc.phone_width, self.dc.phone_height) return True, phone_x, phone_y time.sleep(interval) return False, 0, 0 # test_cases.py import logging from device_controller import DeviceController from element_finder import ElementFinder logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) def test_login(): 测试登录流程 dc DeviceController() ef ElementFinder(dc) logging.info(开始登录测试) # 1. 查找并点击“我的”tab found, x, y ef.find_element(tab_mine.png, timeout5) if found: dc.tap_on_phone(x, y) logging.info(f点击‘我的’ tab坐标({x}, {y})) else: logging.error(未找到‘我的’ tab测试终止) return False time.sleep(2) # 等待页面跳转 # 2. 查找并点击“登录/注册”按钮 found, x, y ef.find_element(btn_login_entry.png) if found: dc.tap_on_phone(x, y) logging.info(f点击登录入口坐标({x}, {y})) else: logging.error(未找到登录入口) return False time.sleep(2) # 3. 查找用户名输入框并点击然后输入文本 found, x, y ef.find_element(input_username.png) if found: dc.tap_on_phone(x, y) # 点击后输入法通常会弹出直接使用ADB输入文本不依赖焦点 dc.adb_text(testuserexample.com) # 假设dc里有这个封装方法 logging.info(f在用户名输入框输入文本) else: logging.error(未找到用户名输入框) return False # 4. 查找密码输入框并点击、输入 # ... 类似操作 ... # 5. 查找并点击“登录”按钮 found, x, y ef.find_element(btn_login_submit.png) if found: dc.tap_on_phone(x, y) logging.info(f点击提交登录坐标({x}, {y})) else: logging.error(未找到登录提交按钮) return False time.sleep(3) # 等待登录结果 # 6. 验证登录成功例如查找用户头像或“退出登录”按钮 found, _, _ ef.find_element(icon_user_avatar.png, timeout5) if found: logging.info(登录成功验证通过) return True else: logging.error(登录成功验证失败) return False finally: dc.stop_scrcpy() # main.py if __name__ __main__: success test_login() if success: print(测试用例执行通过) else: print(测试用例执行失败)5.2 实战编写一个简单的自动化脚本假设我们要自动化一个简单的场景打开某新闻App滑动几次然后点击第一条新闻进入详情页。准备模板图片用Scrcpy投屏手动截图保存以下模板icon_news_app.png(桌面上的新闻App图标)news_item_1.png(列表第一条新闻的局部特征图比如标题开头几个字)btn_back.png(详情页的返回按钮)编写脚本# simple_news_auto.py import time from device_controller import DeviceController from element_finder import ElementFinder dc DeviceController() ef ElementFinder(dc) try: # 0. 回到桌面确保起点一致 dc.adb_keyevent(3) # HOME键 time.sleep(1) # 1. 找到并点击新闻App图标 found, x, y ef.find_element(icon_news_app.png, timeout5) if found: dc.tap_on_phone(x, y) print(已打开新闻App) else: print(未找到新闻App图标) exit(1) time.sleep(3) # 等待App启动 # 2. 模拟滑动浏览滑动3次 screen_center_x dc.phone_width // 2 screen_center_y dc.phone_height // 2 for i in range(3): # 从屏幕中部偏下滑动到中部偏上 start_y int(dc.phone_height * 0.7) end_y int(dc.phone_height * 0.3) dc.swipe_on_phone(screen_center_x, start_y, screen_center_x, end_y, 500) print(f第{i1}次滑动) time.sleep(1.5) # 等待滑动动画和内容加载 # 3. 找到并点击第一条新闻 found, x, y ef.find_element(news_item_1.png, timeout5) if found: dc.tap_on_phone(x, y) print(已进入新闻详情页) else: print(未找到第一条新闻) exit(1) time.sleep(2) # 4. 简单浏览后点击返回 # 可以先做点别的比如等待几秒模拟阅读 time.sleep(3) found, x, y ef.find_element(btn_back.png, timeout5) if found: dc.tap_on_phone(x, y) print(已返回新闻列表) else: # 如果没找到返回按钮用物理返回键 dc.adb_keyevent(4) print(使用物理返回键返回) print(自动化流程执行完毕) except Exception as e: print(f执行过程中发生错误: {e}) finally: dc.stop_scrcpy()这个脚本展示了基本的流程控制、元素查找和操作组合。你可以在此基础上增加更多的检查点、逻辑判断和异常处理构建复杂的测试用例。6. 常见问题、优化技巧与避坑指南在实际使用这套方案时我遇到了不少问题也总结了一些优化技巧。6.1 常见问题与解决方案问题现象可能原因解决方案adb devices显示设备为unauthorized手机未授权电脑的USB调试请求。1. 检查手机屏幕是否有授权弹窗点击“允许”。2. 重启ADB服务adb kill-server adb start-server。3. 更换USB数据线或USB端口。Scrcpy启动后黑屏或连接失败1. 手机未开启USB调试。2. 驱动问题Windows常见。3. 手机系统兼容性问题如ColorOS等定制系统。1. 确认USB调试已开启。2. 安装完整的手机厂商USB驱动。3. 尝试使用Scrcpy的旧版本或添加--force-adb-forward参数。图像识别始终找不到模板1. 模板图片与屏幕截图差异太大亮度、缩放、UI更新。2. 匹配阈值 (threshold) 设置过高。3. Scrcpy窗口大小/比例变化导致坐标映射错误。1. 重新截取模板确保环境一致。对图像进行预处理灰度、二值化。2. 适当降低阈值如从0.9调到0.7。3. 固定Scrcpy启动参数确保脚本获取的窗口尺寸稳定。ADB点击命令执行了但手机没反应1. 坐标计算错误点在了屏幕外或无效区域。2. 手机屏幕处于锁屏或休眠状态。3. 当前应用不接收输入事件如弹窗遮挡。1. 打印出计算的手机坐标用adb shell input tap x y手动验证。2. 发送唤醒命令adb shell input keyevent 26电源键然后adb shell input keyevent 82菜单键解锁如果设置了密码则无效。3. 在操作前先点击一下屏幕中央确保焦点。脚本执行速度慢1. 图像识别模板匹配耗时。2. Scrcpy截图或ADB命令有延迟。3. 循环查找元素的等待间隔 (interval) 太短导致CPU占用高。1. 缩小模板图片尺寸使用灰度图像匹配考虑在非关键路径使用固定坐标。2. 降低Scrcpy画质 (--bit-rate) 和分辨率 (--max-size)。3. 适当增加查找间隔如从0.5秒增加到1秒。在多台设备上运行不稳定不同设备分辨率不同导致坐标映射和模板匹配失效。1.核心方案使用基于比例的坐标和图像识别而非绝对坐标。2. 为不同分辨率设备准备不同的模板图片集。3. 使用更鲁棒的图像特征匹配如ORB代替模板匹配。6.2 性能与稳定性优化技巧固定环境这是保证脚本稳定性的第一要务。使用固定的测试手机或模拟器、固定的分辨率、固定的系统主题和字体大小。避免在脚本运行期间手动操作手机或电脑。降低图像识别依赖不是所有操作都需要图像识别。对于位置固定的元素如底部Tab栏的按钮可以事先计算好其相对于屏幕的比例坐标直接使用比例点击速度极快。# 假设“首页”Tab在屏幕底部横向20%的位置 tab_home_x int(phone_width * 0.2) tab_home_y int(phone_height * 0.95) # 靠近底部 dc.tap_on_phone(tab_home_x, tab_home_y)智能等待代替固定休眠大量使用time.sleep是低效的。应该用“查找元素”函数自带的超时等待机制只在必要时才等待。对于网络加载可以结合查找“加载中”图标消失的逻辑。错误重试机制对于非关键性失败如一次点击没反应可以加入重试逻辑。def robust_tap(element_finder, template_name, retries3): for i in range(retries): found, x, y element_finder.find_element(template_name, timeout2) if found: element_finder.dc.tap_on_phone(x, y) # 点击后可以再查找一个预期出现的元素来确认点击成功 time.sleep(0.5) if element_finder.find_element(expected_element_after_tap.png, timeout2)[0]: return True print(f第{i1}次点击尝试失败或未验证成功) return False日志与截图在关键步骤和失败时保存当时的屏幕截图和日志。这对于后期调试和生成测试报告至关重要。可以用OpenCV的cv2.imwrite保存截图并打上时间戳和步骤名称。6.3 与主流框架的对比与适用场景最后我们来客观看待这个方案明确它的最佳适用场景。对比Appium优势启动快、资源占用小、执行速度极快、环境依赖简单只需ADB和Scrcpy、更底层直接。劣势生态弱没有现成的Page Object模型、丰富的客户端库元素定位主要依赖图像对UI变化更敏感跨平台能力弱主要针对Android。对比Airtest优势更轻量Airtest其实也封装了不少东西执行逻辑更透明可控结合Python自由度极高。劣势Airtest提供了IDE和一站式图像识别方案上手更快。本方案需要自己搭建框架。适用场景Android应用的冒烟测试/核心链路回归需要快速执行验证主流程是否通畅。重复性高的简单操作自动化如批量安装/卸载应用、清理缓存、简单的UI遍历。对执行速度有严格要求的场景。作为现有测试框架的补充在Appium脚本中穿插使用ADB命令来完成一些特定操作如截图、拉取日志文件、修改系统设置。测试开发人员构建轻量级定制化测试工具。不适用场景需要精确控件定位和属性验证的复杂测试Appium的UIAutomator2/XCUITest驱动在这方面是专业且稳定的。跨平台iOS/Android测试本方案严重依赖ADB是Android专属。对测试脚本可维护性、团队协作要求极高的中大型项目Appium的生态和模式更适合。我个人在实际项目中经常将这种轻量级方案用于开发自测和预合入验证。开发完成后跑一个5分钟以内的脚本快速过一遍核心功能比手动测试高效得多。而对于正式的CI/CD流水线中的UI自动化我仍然会使用更稳定、可维护性更强的Appium框架。工具没有好坏只有是否适合当下的场景。希望这个详细的分享能为你提供一种新的、高效的自动化测试思路和一套可以直接上手实践的工具方法。