Skip to main content

Multi-layer task-stack navigation framework for Android ADB automation. Define N layers, register per-layer handlers, and let the framework handle BACK recovery, cold-start, cross-layer verification, and sub-page navigation (detect_detail + _recover_to_page).

Project description

LayerNav_Android

A stable page layer navigation framework for Android ADB automation. 基于 ADB 的安卓页面层级导航框架,主打强校验、自动容错、故障恢复,适用于 APP 数据采集、移动端 RPA、UI 自动化测试场景。

Python 3.12+ · 零外部依赖 · MIT License

PyPI Python CI codecov License


一、项目介绍

市面上主流 ADB / UI 自动化库仅提供点击、滑动、返回等基础原子能力,缺少页面状态校验、层级管理、异常恢复。本框架基于 L0~Ln 页面层级模型 设计,将桌面、APP主页、内容页、详情页抽象为标准化层级,内置「动作执行 → 截屏校验 → 自动重试 → 冷启动恢复」全链路能力。

核心定位

  • 导航调度 + 容错引擎
  • 框架负责:层级检测、跳转校验、后退/前进/恢复、子页面导航
  • 业务脚本负责:控件点击、数据采集、业务逻辑

二、核心特性

标准化层级模型 统一抽象 L0(手机桌面) / L1(APP主页) / L2(内容页) / L3(详情页) ... Ln,一套模型适配绝大多数 APP。

闭环跳转校验 执行操作后自动截屏检测页面,拒绝盲操作,跳转失败即时感知。guard(前置校验)+ validator(后置轮询)语义分离。

同层多页面导航(v0.3.0) detect_detail() 一次截图返回层级 + 子页面;back_recover / back / restore 支持 target_page 参数,恢复后自动精确定位到指定子页面。

完整导航原子 API 内置 detect / enter_next / back_one / back_recover 四原子操作 + advance / back / restore 三组合操作,一行代码完成跨层级跳转。

故障自动恢复 返回键失效、页面卡死、意外退回桌面时,自动执行 back_recover:HOME → 冷启动 APP → 快速前进至目标层级 → 正常恢复业务。

Quick 快速模式 专为恢复场景设计,handlers 收到 quick=True 时可精简业务逻辑(如选第一个未读),提升导航速度。

可观测监听器 内置 LayerListener 观察者接口,零侵入监控层切换、超时、恢复事件,方便接入指标采集与告警。

解耦设计

  • 页面检测 detect 接口可自由接入:OCR / 图像匹配 / UI 控件解析
  • ADB 客户端通过 AdbProtocol 完全抽象,原生 ADB / 风控加固 ADB 均可无缝接入
  • 分层 Handler 机制(_on_Lx),业务代码与框架逻辑完全隔离

三、架构设计

1. 层级模型

L0     手机主屏幕(非 APP 前台)
L1     APP 主界面
L2     二级内容页
L3     三级详情页
...
Ln     最深业务层级

同层多页面支持(v0.3.0)

同一个层级可包含多个子页面(如 L1 的会话列表/通讯录/发现/我)。LayerDef 通过 page_namedetection_extra 字段声明:

layers = [
    LayerDef("L1", "main_list", "微信主界面", "is_main_list_chrome()",
             page_name="chat_list",
             detection_extra="子页面: chat_list/contacts/discover/profile"),
]
能力 方法 说明
子页面检测 detect_detail() → DetectResult 返回 (layer_key, page_name)
子页面恢复 restore(..., target_page="chat_list") 到达目标层后调用 _recover_to_page
Tab 切换(L1) WeChatGroupLayerModel._recover_to_page 框架自动计算底部 tab 坐标并点击
校验型(L2/L3) detect_detail 验证 无需额外操作,校验 page_name 即可

2. 职责划分

模块 框架能力 Task 能力
状态检测 调用 detect() / detect_detail()、校验结果 实现截图/识别逻辑
页面动作 流程调度、等待、重试 实现 _on_Lx 点击/滑动等业务动作
导航逻辑 advance / back / restore / 恢复
子页面导航 detect_detail / _recover_to_page 无(框架提供 target_page 路由)
点击 不负责 — 框架不 tap handler 内 adb.tap()

3. 核心流程

1. detect() 实时识别当前页面层级
2. 调用对应层级 _on_Lx handler 执行业务操作
3. 二次校验页面是否到达目标层级(截屏 + 轮询)
4. 跳转失败 → 自动重试 → 重试失败 → 冷启动恢复

四、快速上手

1. 安装

方式一:pip 安装(推荐)

pip install layernav_android

如需使用 WeChat contrib 模块,需额外安装 opencv-pythonnumpy

pip install opencv-python numpy

方式二:从源码安装

git clone https://github.com/yuyidream/layernav_android.git
cd layernav_android
pip install -e .

如需运行测试:

pip install -e ".[dev]"
pytest tests/ -v --tb=short

2. 基础使用

继承 BaseLayerModel,实现层级检测与页面处理器,即可使用全套导航能力:

from layernav_android import BaseLayerModel, LayerDef

class DemoAppModel(BaseLayerModel):
    layers = [
        LayerDef(key="L0", name="desktop",   label_cn="手机桌面", detection="截屏识别桌面图标"),
        LayerDef(key="L1", name="app_home",  label_cn="APP 主页", detection="OCR 识别主页文字",
                 page_name="home", detection_extra="子页面: home/search"),
        LayerDef(key="L2", name="content",   label_cn="内容列表页", detection="图像特征匹配"),
        LayerDef(key="L3", name="detail",    label_cn="详情页",   detection="模板匹配"),
    ]

    def detect(self, adb, scale_w: float) -> str:
        screenshot = adb.screencap()
        if is_desktop(screenshot):
            return "L0"
        elif is_app_home(screenshot):
            return "L1"
        elif is_content_list(screenshot):
            return "L2"
        return "L3"

    def _on_L0(self, adb, scale_w, *, quick=False):
        self._cold_start(adb, "L1", scale_w)
        return "L1"

    def _on_L1(self, adb, scale_w, *, quick=False) -> str | None:
        if quick:
            row = self._pick_first_row(adb, scale_w)
        else:
            row = self._scan_and_select(adb, scale_w)
        if row is None:
            return None
        adb.tap(row.x, row.y)
        return "L2"

    def _on_L2(self, adb, scale_w, *, quick=False) -> str | None:
        item = self._pick_item(adb, scale_w, quick=quick)
        if item is None:
            return None
        adb.tap(item.x, item.y)
        return "L3"

    def _on_L3(self, adb, scale_w, *, quick=False) -> str | None:
        return None  # 最深层,不再前进


# 执行导航流程
model = DemoAppModel()
adb = get_adb_client()

# 检测层级 + 子页面
dr = model.detect_detail(adb, scale_w=1.0)
print(f"当前: {dr.layer_key} / {dr.page_name}")

# 智能恢复到 L1 的 home 子页面
model.restore(adb, target_layer="L1", scale_w=1.0, target_page="home")
# 逐层前进到 L3
model.advance(adb, target_layer="L3", scale_w=1.0)
# 后退回 L1 的 search 子页面
model.back(adb, to_layer="L1", scale_w=1.0, target_page="search")

3. 核心 API

原子操作

方法 说明
detect(adb, scale_w) → str 检测当前所在层级(Task 覆盖实现)
detect_detail(adb, scale_w) → DetectResult 检测层级 + 子页面名称(v0.3.0,默认调用 detect + LayerDef.page_name
enter_next(adb, scale_w, *, quick, max_wait_s) → bool 单步进入下一层 ← guard + validator + 轮询
back_one(adb, scale_w) → str 单步 KEYCODE_BACK,返回新层级
back_recover(adb, target, scale_w, *, target_page=None) → bool 故障恢复:HOME → 冷启动 → 快速前进 → 子页面。v0.4.3: 冷启动 3 次重试 + adb reboot 兜底;支持 target_page(v0.4.3: 重启安卓系统作为终极兜底)

组合操作

方法 说明
back(adb, to_layer, scale_w, *, target_page=None) → bool 逐层后退至目标(3 次重试 → 恢复)(v0.3.0: 支持 target_page
advance(adb, target, scale_w, *, quick, max_wait_s) → bool 逐层前进至目标(目标层 always quick=False
restore(adb, target, scale_w, *, target_page=None) → bool 智能判断方向,从任意位置恢复至目标 + 子页面**(v0.3.0: 支持 target_page)**

可观测

from layernav_android import LayerListener

class MetricsListener:
    def on_transition(self, from_layer, to_layer, method):
        print(f"{from_layer}{to_layer} via {method}")

    def on_timeout(self, from_layer, target_layer, elapsed_s):
        print(f"Timeout {from_layer}{target_layer} after {elapsed_s:.1f}s")

    def on_recovery(self, target_layer, ok):
        print(f"Recovery to {target_layer}: {'OK' if ok else 'FAILED'}")

model.add_listener(MetricsListener())

五、通用冷启动工具

cold_start_app_from_launcher 提供统一的 APP 冷启动能力,支持 monkey 主路径 + Dock 图标兜底 + session tab 点击:

from layernav_android.cold_start import cold_start_app_from_launcher

# 微信 — 最简调用(尺寸自动获取)
ok = cold_start_app_from_launcher(
    adb, "com.tencent.mm",
    app_name="wechat", M=4, N=3,
)

# 微信 — 含 session tab
ok = cold_start_app_from_launcher(
    adb, "com.tencent.mm",
    app_name="wechat", M=4, N=3,
    session_tab_x=108, session_tab_y=2192,
)

# 小红书
ok = cold_start_app_from_launcher(
    adb, "com.xingin.xhs",
    app_name="xhs", M=4, N=1,
)

关键设计:使用普通 ADB tap(非防风控触控),因为是系统级操作(桌面 Dock 图标点击),不涉及 APP 内反爬检测,方便所有系统集成。

最后一搏 — adb reboot 兜底allow_reboot=True,默认关闭):

当 monkey、am start、Dock icon tap 三条路径全部失败时,可选执行 adb reboot 作为终极恢复手段。重启后等待设备上线 + boot 完成,然后重新尝试 monkey 启动。

⚠️ 重启耗时 60–120 s,且要求设备无需手动解锁(无 PIN/图案锁)。适用于无人值守的 7×24 自动化。

六、适用场景

  • 移动端 RPA 自动化 — 加速开发
  • Android UI 自动化测试 — 提升脚本稳定性,减少维护成本
  • APP 流程逆向 / 行为模拟 — 稳定进入深层页面

七、优势对比

能力 本框架 Appium / uiautomator2 / Airtest
标准化页面层级 ✅ 内置模型 ❌ 无统一抽象
同层多页面导航 detect_detail + _recover_to_page ❌ 需手动分支
操作后页面校验 ✅ 闭环 guard + validator ❌ 仅执行动作,不校验结果
自动后退恢复 ✅ 3 次重试 + 冷启动兜底 ❌ 需手动编写重试逻辑
层级穿越 API advance / back / restore ❌ 仅基础点击/返回
可观测监听器 LayerListener 事件回调 ❌ 需自行埋点
ADB 解耦 AdbProtocol 接口抽象 ⚠️ 部分耦合

八、拓展建议

  • 页面检测能力:可接入 PaddleOCR / EasyOCR / OpenCV 图像匹配
  • 状态机拓展:可结合 python-statemachine 优化状态管理(本框架的 LayerListener 即借鉴其设计)
  • 多设备并行:每设备独立 BaseLayerModel 实例即可天然支持多设备

九、目录结构

layernav_android/
├── src/layernav_android/
│   ├── __init__.py          # 公开导出
│   ├── _protocol.py         # AdbProtocol 接口
│   ├── base.py              # LayerDef, LayerListener, BaseLayerModel
│   ├── cold_start.py        # 通用冷启动工具(含 adb reboot 兜底)
│   └── contrib/
│       ├── __init__.py
│       ├── wechat.py        # WeChatGroupLayerModel(微信示例)
│       └── xhs.py           # XhsLayerModel(小红书占位)
├── tests/
│   └── test_base.py         # 23 个单元测试
├── pyproject.toml
├── README.md
└── LICENSE

十、参与贡献

欢迎提交 Issue、PR,共建安卓自动化导航生态:

  • Bug 反馈、功能建议Issues
  • 代码优化、新增示例 → Pull Request

十一、开源协议

本项目基于 MIT License 开源,可自由用于个人、商业项目。

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

layernav_android-0.4.3-py3-none-any.whl (21.1 kB view details)

Uploaded Python 3

File details

Details for the file layernav_android-0.4.3-py3-none-any.whl.

File metadata

File hashes

Hashes for layernav_android-0.4.3-py3-none-any.whl
Algorithm Hash digest
SHA256 495e7776643e671054b4a3f86131ef7f35b34837b8c4a6e33560fa2dc7ddf6c4
MD5 82aa2d0db415bf184e73d82321c4db19
BLAKE2b-256 5ac226205e84524f3402d0963dc3756785be2f11238ff0fbd885f774f50ea62f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page