- 测试
- 质量保障
- 计算机视觉
【免费下载链接】Airtest
UI Automation Framework for Games and Apps
导读:本文基于 Airtest 官方 API 参考文档 airtest.core.android.rotation.rst 所对应的模块文档,深入剖析
airtest.core.android.rotation模块的完整设计。你将掌握 Airtest 如何通过RotationWatcher实时监听 Android 设备屏幕旋转、如何通过XYTransformer在横竖屏切换时完成坐标换算,并了解方向信息如何驱动截图、触摸与录制功能,从而在游戏与应用 UI 自动化脚本中正确处理旋转场景。
模块定位:负责屏幕方向的感知与坐标换算
airtest.core.android.rotation是 Airtest Android 自动化体系中的方向处理模块,其职责可以概括为两件事:
- 感知方向:通过
RotationWatcher类实时监听设备屏幕旋转,并向注册的回调推送最新的方向值(0、1、2、3,分别对应旋转 0°、90°、180°、270°)。 - 换算坐标:通过
XYTransformer类在“原始物理屏幕坐标”与“竖屏(upright)截图坐标”之间互相转换,保证横竖屏切换后触摸、断言等操作仍然准确。
从源码结构看,该模块被 android.py 在构造 Android 设备对象时实例化并集成(self.rotation_watcher = RotationWatcher(self.adb, self.ori_method),见 android.py#L66),同时被 minicap.py 注册方向变化回调以触发画面重连,是整个 Android 方向体系的中枢。
方向获取的两种方案:ORI_METHOD
方向值本身由设备端提供,但获取途径有两种,定义在 constant.py 的ORI_METHOD类中:
| 常量 | 值 | 含义 |
|---|---|---|
ORI_METHOD.ADB | "ADBORI" | 通过 adb 命令轮询设备方向 |
ORI_METHOD.MINICAP | "MINICAPORI" | 通过 rotationwatcher 服务推送方向(默认方案) |
Android设备对象构造函数的ori_method参数默认取ORI_METHOD.MINICAP(见 android.py#L43),即优先使用高精度的 rotationwatcher 服务;只有在服务安装或启动失败时,才会按源码中的降级逻辑自动回退到ADBORI(见 rotation.py#L109-L116):
if self.ori_method == ORI_METHOD.MINICAP: try: self.setup_server() except: LOGGING.error("RotationWatcher setup failed, use ADBORI instead.") self.ori_method = ORI_METHOD.ADB这意味着即使设备不支持 minicap 相关能力,Airtest 也能依靠 adb 方案继续工作,保证兼容性。
RotationWatcher:实时方向监听器
RotationWatcher是模块的核心类,负责在设备上部署 rotationwatcher 服务、维护后台监听线程并分发方向变化事件。其完整 API 如下:
| 方法 | 作用 |
|---|---|
install() | 推送rotationwatcher.jar到设备并赋权 |
uninstall() | 从设备删除 jar 文件 |
setup_server() | 启动设备端 app_process 服务进程 |
start() | 启动守护线程并返回初始方向值 |
teardown() | 停止进程、清空回调、注销清理钩子 |
reg_callback(cb) | 注册方向变化回调,回调参数为方向值 ori |
部署与启动流程
install()先将本地静态资源 rotationwatcher.jar 推送至设备/data/local/tmp/rotationwatcher.jar,并通过chmod 755赋予执行权限;若目标文件已存在且大小一致,则跳过安装以节省时间(见 rotation.py#L34-L55)。
setup_server()使用app_process方式在设备上运行 Java 服务(见 rotation.py#L80-L90):
p = self.adb.start_shell( "app_process -Djava.class.path={0} /data/local/tmp com.example.rotationwatcher.Main".format( self.path_in_android))服务启动后,其标准输出被包装为NonBlockingStreamReader,用于异步读取方向数据。如果进程立即退出,说明服务可能已被其他连接占用,此时抛出RuntimeError提示 setup 失败。
守护线程与两种刷新模式
start()会创建名为rotationwatcher的守护线程持续运行(见 rotation.py#L139-L166),并根据当前ori_method选择刷新来源:
- MINICAP 模式:从
nbsp.readline()读取服务推送的数据。设备产生旋转时读到形如b"90\r\n"的数据,换算为ori = int(int(line) / 90)(即 90° 对应方向值 1);平时读到空数据返回None;进程结束时读到b"",触发teardown()。 - ADB 模式:调用
self.adb.getDisplayOrientation()轮询,且只有当方向值发生变化时才继续处理(未变化时time.sleep(3)后跳过),避免无意义的重复回调。
线程每轮都会将新方向通过所有已注册回调广播出去,并在进程退出(is_exiting())时自动执行teardown()。初始化时start()会先同步读取一次当前方向作为current_orientation返回,供上层立即使用。
回调注册机制
reg_callback(ow_callback)负责登记方向变化的监听者(见 rotation.py#L170-L181),回调统一约定:参数一定是 ori,若服务断掉则传None。该机制使方向事件能够被多个组件同时消费:
- Android 设备对象用它刷新
_current_orientation(android.py#L1082); - Minicap 采集组件用它触发画面重连(minicap.py#L61):
rotation_watcher.reg_callback(lambda x: self.update_rotation(x * 90)),因为 minicap 在横竖屏切换时必须重新连接数据流。
teardown()会设置线程退出事件、杀掉服务进程、清空回调列表并注销全局清理钩子(reg_cleanup),确保脚本退出或设备断开时不留残留进程(rotation.py#L92-L99)。
方向值语义与坐标变换:XYTransformer
XYTransformer是一个纯静态工具类,负责在“竖屏截图坐标(upright)”与“原始物理屏幕坐标(original)”之间做二维坐标变换,其核心依据是方向值orientation:
| orientation | 含义 | 旋转角度 |
|---|---|---|
| 0 | 竖屏 | 0° |
| 1 | 右横屏 | 90° |
| 2 | 倒竖屏 | 180° |
| 3 | 左横屏 | 270° |
up_2_ori:截图坐标 → 物理坐标
up_2_ori(tuple_xy, tuple_wh, orientation)将竖屏截图上的坐标换算为设备物理屏幕坐标(rotation.py#L188-L211):
if orientation == 1: x, y = w - y, x elif orientation == 2: x, y = w - x, h - y elif orientation == 3: x, y = y, h - x return x, y该方法是触摸坐标换算的关键环节。在 android.py#L1084-L1101 中,_touch_point_by_orientation正是调用它把图像识别得到的坐标转换为真实物理坐标,再交由 minitouch / maxtouch 执行点击。坐标原点约定为设备物理屏幕的左上角。
ori_2_up:物理坐标 → 截图坐标
ori_2_up(tuple_xy, tuple_wh, orientation)执行反向换算(rotation.py#L213-L236):
if orientation == 1: x, y = y, w - x elif orientation == 2: x, y = w - x, h - y elif orientation == 3: x, y = h - y, x return x, y两个方法互为逆运算,当方向值为 0 时坐标不做任何变换,直接返回原值。
方向信息的下游消费链路
从源码集成看,方向值最终会转化为设备对外暴露的统一状态:
- display_info 更新:在 android.py#L820-L827 中,
display_info属性会优先采用 rotation_watcher 的实时方向(注释明确指出 "update ow orientation, which is more accurate"),写入"rotation"(角度制,ori * 90)与"orientation"(方向值)两个字段。测试 test_android.py#L39-L40 断言display_info中同时包含orientation与rotation键。 - 分辨率换算:
get_current_resolution()在方向为 1/3(横屏)时交换宽高(android.py#L848-L852),get_render_resolution()也会对渲染偏移量做同样的宽高交换(android.py#L872-L874)。 - 生命周期管理:
disconnect()按顺序执行 minicap/javacap 停止 → minitouch/maxtouch 停止 →rotation_watcher.teardown()(android.py#L1120-L1134),保证断开设备时方向服务一并释放。
ADB 方案的底层实现:getDisplayOrientation
当使用ORI_METHOD.ADB时,方向值来自 adb.py#L1204-L1235 的getDisplayOrientation(),该方法针对不同系统版本实现了多级回退:
- 解析
dumpsys SurfaceFlinger输出中的orientation=(\d+),兼容老设备(注释提到适用于 SDK 15 的三星旧机型); - 回退解析
dumpsys input中的SurfaceOrientation:\s+(\d+); - 再回退解析
dumpsys window displays中的DisplayFrames.*r=(\d+); - 全部失败时打印警告并返回 0。
这套多级探测策略保证了在缺乏 minicap 服务能力的环境下,仍能稳定获得方向数据。
测试覆盖与验证
仓库测试对 rotation 模块的集成路径提供了直接验证:
- test_minicap.py:先调用
rotation_watcher.get_ready()启动方向服务,再将rotation_watcher注入Minicap,测试结束时调用teardown();其中test_rotation用例直接验证旋转场景。 - test_screen_proxy.py:
ScreenProxy.auto_setup以rotation_watcher=self.dev.rotation_watcher方式接入方向服务。 - test_touch_proxy.py:触摸代理通过
ori_transformer=self.dev._touch_point_by_orientation接入坐标换算链路。
这些用例印证了方向监听与坐标变换并非孤立功能,而是深度嵌入采集(cap)、触摸(touch)与屏幕代理(screen proxy)的完整链路。
实战要点与最佳实践
- 默认无需干预:使用
connect_device("android:///")或Android()创建设备对象时,rotation 服务会自动初始化(ori_method默认MINICAPORI),脚本层通常无需手动调用rotation_watcher的接口。 - 横竖屏切换的坐标安全:截图中识别到的坐标会经
_touch_point_by_orientation自动换算为物理坐标,因此脚本中始终以竖屏截图坐标为基准编写,旋转发生时无需修改脚本逻辑。 - 需要手动接入时:参考 screen_proxy.py#L83 的用法,将
rotation_watcher显式传给需要感知方向的组件。 - 自定义方向回调:可通过
dev.rotation_watcher.reg_callback(lambda x: ...)注册自己的监听函数,但要注意回调运行在独立线程中,需自行保证线程安全(源码注释明确提示 "callback is called in another thread, so be careful about thread-safety")。 - 方向与录屏:
start_recording的orientation参数(1 竖屏、2 横屏、0 跟随旋转)与方向体系相互独立,可按录制需求单独指定(见 android.py#L890 及 test_android_recorder.py#L113-L121)。
小结
airtest.core.android.rotation模块以极简的 API 封装了 Android 屏幕旋转的完整处理链:RotationWatcher负责方向感知与事件分发(MINICAP 服务优先、ADB 轮询兜底),XYTransformer负责横竖屏坐标的双向换算,二者共同保障了 Airtest 在任意旋转状态下截图、点击、断言与录屏的一致性。理解这一模块,是编写稳定横竖屏 UI 自动化脚本的前提。如需进一步阅读,可深入 rotation.py、android.py 与 constant.py 的源码实现。
- 测试
- 质量保障
- 计算机视觉
【免费下载链接】Airtest
UI Automation Framework for Games and Apps
相关推荐
深入解析 VictoriaMetrics 依赖的 Azure azidentity 模块:Go 语言 Microsoft Entra ID 凭证体系实战指南
深入解析 VictoriaMetrics 依赖的 Azure azidentity 模块:Go 语言 Microsoft Entra ID 凭证体系实战指南 本
测试质量保障计算机视觉lo 泛型类型操作(Type Manipulation)核心辅助函数全解析:nil 检测、指针转换与 Coalesce 家族
lo 泛型类型操作(Type Manipulation)核心辅助函数全解析:nil 检测、指针转换与 Coalesce 家族 lo 是基于 Go 1.18+ 泛
测试质量保障计算机视觉QtScrcpy屏幕旋转处理指南:VideoForm实现横竖屏动态切换
QtScrcpy屏幕旋转处理指南:VideoForm实现横竖屏动态切换 QtScrcpy是一款强大的Android实时投屏软件,提供USB和TCP/IP连接的A
桌面应用音视频
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考