Skip to content

async-adbc v2.0 重构方案

文档版本: 1.0
创建日期: 2026-05-01
状态: 待审核

1. 概述

本文档描述 async-adbc 项目从 v1.x 到 v2.0 的重构计划,包括当前代码结构分析、存在的问题、重构目标和实施方案。

2. 当前结构分析

2.1 目录结构

async_adbc/
├── __init__.py              # 导出 ADBClient, Device, Status
├── adbclient.py             # ADBClient 类 (继承 HostService)
├── device.py                # Device 类 (继承 LocalService) + Status 枚举
├── exceptions.py            # 异常导出 (但定义分散在各模块)
├── plugin.py                # Plugin 基类
├── protocol.py              # Connection, Response, 协议常量
├── service/
│   ├── __init__.py          # Service 抽象基类
│   ├── host.py              # HostService + 数据模型 + 异常
│   └── local.py             # LocalService
└── plugins/                 # 各功能插件
    ├── __init__.py
    ├── pm.py                # PMPlugin + InstallError 等
    ├── prop.py
    ├── cpu.py               # CPUPlugin + 数据模型
    ├── gpu.py
    ├── mem.py               # MemPlugin + 数据模型
    ├── fps.py               # FpsPlugin + SurfaceNotFoundError + FpsStat
    ├── battery.py
    ├── temp.py
    ├── traffic.py
    ├── forward.py
    ├── am.py
    ├── logcat.py
    ├── minicap.py
    ├── wm.py
    ├── input.py
    └── utils.py

2.2 依赖关系图

adbclient.py ──┐
               ├──> protocol.py
device.py ─────┤
               ├──> service/
plugins/* ─────┤
               └──> plugin.py

循环依赖风险: - service/host.py 导入 device.py 中的 DeviceStatus - device.py 导入 service/host.py 中的 ADBClient (类型注解用)

3. 存在的问题

3.1 代码组织问题

问题 严重程度 说明
职责混杂 🔴 高 service/host.py 包含服务实现、数据模型、异常定义
硬编码插件 🔴 高 Device.__init__ 中手动实例化所有插件,无法扩展
异常分散 🟡 中 异常定义在各插件模块中,通过 exceptions.py 聚合导出
模型分散 🟡 中 Pydantic 模型分散在各插件和 service 模块中
单文件过大 🟡 中 protocol.py 包含多个类和常量,可拆分

3.2 插件系统问题

  1. 缺少自动注册机制
  2. 新增插件需要手动修改 Device.__init__plugins/__init__.py
  3. 不符合开闭原则 (OCP)

  4. 插件发现不灵活

  5. 无法动态启用/禁用插件
  6. 无法自定义插件加载顺序

3.3 类型注解问题

  • 大量使用 typing.TYPE_CHECKINGcast()
  • 模块间循环依赖导致类型注解复杂
  • device.pyADBClient 类型注解需要 forward reference

3.4 可维护性问题

问题 说明
协议模块过大 protocol.py 500+ 行,包含常量、Connection、Response
测试耦合 测试依赖实际设备连接,难以单元测试
配置硬编码 DEFAULT_HOST, DEFAULT_PORT 等常量散落在代码中

3.5 命名问题

  • adbclient.py → 建议重命名为 client.py
  • rowraw (拼写错误,见于 shell_row 应为 shell_raw)

4. 重构目标

4.1 核心目标

  1. 清晰的职责分离 - 每个模块只负责一个功能
  2. 可扩展的插件系统 - 支持自动发现和注册插件
  3. 更好的可测试性 - 降低模块间耦合
  4. 类型安全 - 减少 TYPE_CHECKING 和 cast 使用

4.2 非目标

  • 不改变对外 API (保持向后兼容)
  • 不重写协议实现
  • 不重构测试代码 (除非必要)

5. 目标架构

5.1 新目录结构

async_adbc/
├── __init__.py
├── client.py                    # ADBClient (重命名)
├── device.py                    # Device (使用插件注册表)
├── exceptions.py                # 所有异常统一定义
├── plugin.py                    # Plugin基类 + 注册表
├── config.py                    # 配置常量
├── protocol/
│   ├── __init__.py
│   ├── consts.py                # 协议常量 (OKAY, FAIL, DATA 等)
│   ├── connection.py            # Connection 类
│   └── response.py              # Response 类
├── service/
│   ├── __init__.py
│   ├── base.py                  # Service 抽象基类
│   ├── host.py                  # HostService (纯服务实现)
│   └── local.py                 # LocalService
├── plugins/
│   ├── __init__.py              # 使用注册表自动发现
│   ├── _registry.py             # 插件注册表 (内部)
│   ├── base.py                  # 插件基类别名
│   ├── pm.py
│   ├── prop.py
│   ├── cpu.py
│   ├── gpu.py
│   ├── mem.py
│   ├── fps.py
│   ├── battery.py
│   ├── temp.py
│   ├── traffic.py
│   ├── forward.py
│   ├── am.py
│   ├── logcat.py
│   ├── minicap.py
│   ├── wm.py
│   ├── input.py
│   └── utils.py
└── models.py                    # 所有 Pydantic 模型统一定义

5.2 模块职责说明

模块 职责
config.py 存放全局常量 (DEFAULT_HOST, DEFAULT_PORT 等)
exceptions.py 所有异常类的唯一定义位置
models.py 所有 Pydantic 数据模型 (ForwardRule, FpsStat, CPUInfo 等)
plugin.py Plugin 基类 + 装饰器 + 注册表
protocol/ 拆分后的协议实现
plugins/_registry.py 插件注册表实现 (内部模块)

5.3 依赖流向

config.py ──┐
exceptions.py ─┐
              ├──> models.py
plugin.py ─────┤
               ├──> protocol/
service/ ──────┤
               ├──> plugin.py (注册表)
client.py ─────┤
               └──> service/
device.py ─────┘

6. 详细实施方案

6.1 阶段一: 整理数据模型和异常

目标: 将分散的模型和异常集中管理

6.1.1 创建 models.py

从以下位置迁移模型: - service/host.py: DeviceStatusNotification, ForwardRule, ReverseRule - plugins/fps.py: FpsStat - plugins/cpu.py: CPUInfo, CPUUsage, CPUFreq, CPUStat, ProcessCPUStat - plugins/mem.py: MemInfo, MemStat

6.1.2 重构 exceptions.py

从以下位置迁移异常: - service/host.py: DeviceNotFoundError - plugins/pm.py: InstallError, UninstallError, ClearError - plugins/fps.py: SurfaceNotFoundError

6.2 阶段二: 拆分 protocol.py

目标: 将大文件拆分为职责单一的小文件

6.2.1 创建 protocol/consts.py

HEADER_LENGTH = 4
OKAY = "OKAY"
FAIL = "FAIL"
STAT = "STAT"
# ... 其他常量

6.2.2 创建 protocol/connection.py

  • Connection
  • encode_length(), decode_length(), pack() 函数
  • create_connection() 函数

6.2.3 创建 protocol/response.py

  • Response

6.2.4 兼容处理

保留 protocol.py 作为导出模块,避免破坏现有导入:

# async_adbc/protocol.py (兼容层)
from .protocol.consts import *  # noqa
from .protocol.connection import Connection, create_connection  # noqa
from .protocol.response import Response  # noqa

6.3 阶段三: 实现插件注册表

目标: 实现自动插件发现和注册机制

6.3.1 设计插件元数据

# plugin.py
from dataclasses import dataclass
from typing import Type

@dataclass
class PluginMetadata:
    name: str           # 插件名称,如 "pm", "fps"
    attr_name: str      # Device 属性名,如 "pm" → device.pm
    plugin_class: Type['Plugin']
    enabled: bool = True

6.3.2 实现注册表

# plugins/_registry.py
from typing import Dict, List, Type
from async_adbc.plugin import Plugin, PluginMetadata

class PluginRegistry:
    def __init__(self):
        self._plugins: Dict[str, PluginMetadata] = {}

    def register(self, name: str, attr_name: str, plugin_class: Type[Plugin]):
        self._plugins[name] = PluginMetadata(
            name=name,
            attr_name=attr_name,
            plugin_class=plugin_class
        )

    def get_all(self) -> List[PluginMetadata]:
        return [p for p in self._plugins.values() if p.enabled]

# 全局注册表实例
_registry = PluginRegistry()

6.3.3 注册装饰器

# plugin.py
from typing import Type
from .plugins._registry import _registry

def register_plugin(name: str, attr_name: str):
    def decorator(cls: Type[Plugin]):
        _registry.register(name, attr_name, cls)
        return cls
    return decorator

6.3.4 改造插件

# plugins/pm.py
from async_adbc.plugin import Plugin, register_plugin

@register_plugin("pm", "pm")
class PMPlugin(Plugin):
    # ... 现有实现

6.4 阶段四: 重构 Device 类

目标: 使用注册表动态加载插件

6.4.1 新的 Device.init

# device.py
from .plugins._registry import _registry

class Device(LocalService):
    def __init__(self, adbc: "ADBClient", serialno: str) -> None:
        self.adbc = adbc
        self.serialno = serialno

        # 动态加载插件
        self._load_plugins()

    def _load_plugins(self):
        for metadata in _registry.get_all():
            plugin = metadata.plugin_class(self)
            setattr(self, metadata.attr_name, plugin)

6.4.2 延迟加载 (可选优化)

考虑实现插件的延迟加载,减少初始化开销:

def _load_plugins(self):
    self._plugin_cache = {}
    for metadata in _registry.get_all():
        self._plugin_cache[metadata.attr_name] = metadata

def __getattr__(self, name):
    if name in self._plugin_cache:
        metadata = self._plugin_cache[name]
        plugin = metadata.plugin_class(self)
        setattr(self, name, plugin)
        return plugin
    raise AttributeError(f"'Device' object has no attribute '{name}'")

6.5 阶段五: 创建 config.py

# config.py
DEFAULT_HOST = "127.0.0.1"
DEFAULT_PORT = 5037
TEMP_PATH = "/data/local/tmp"
DEFAULT_CHMOD = 0o644
DATA_MAX_LENGTH = 65536

修改使用这些常量的模块从 config.py 导入。

6.6 阶段六: 清理 service 模块

6.6.1 service/base.py

service/__init__.py 中的 Service 抽象基类移到 service/base.py

6.6.2 service/host.py

  • 移除 DeviceNotFoundError (已移到 exceptions.py)
  • 移除数据模型类 (已移到 models.py)
  • 保留纯服务实现

6.6.3 兼容处理

# service/__init__.py (兼容层)
from .base import Service  # noqa

6.7 阶段七: 重命名 adbclient.py

重命名为 client.py,保持兼容:

# adbclient.py (兼容层)
from .client import ADBClient  # noqa

__all__ = ["ADBClient"]

7. 迁移指南

7.1 对插件开发者

旧方式:

# 1. 创建插件类
class MyPlugin(Plugin): ...

# 2. 在 plugins/__init__.py 导出
from .myplugin import MyPlugin

# 3. 在 device.py 手动添加
self.myplugin = MyPlugin(self)

新方式:

from async_adbc.plugin import Plugin, register_plugin

@register_plugin("myplugin", "myplugin")
class MyPlugin(Plugin): ...

7.2 对库用户

对外 API 保持不变:

# 仍然可以正常使用
from async_adbc import ADBClient, Device

adbc = ADBClient()
device = await adbc.device()

await device.pm.list_packages()  # 不变
await device.fps.stat(...)       # 不变

8. 测试策略

8.1 保持测试通过

  • 重构过程中确保现有测试持续通过
  • 使用 git bisect 定位引入问题的提交

8.2 新增测试

  • 为插件注册表添加单元测试
  • 为新的模块组织结构添加导入测试

8.3 兼容性测试

  • 测试旧的导入路径仍然工作
  • 测试第三方插件的注册和使用

9. 风险评估

风险 概率 影响 缓解措施
引入 bug 🟡 中 🔴 高 每阶段完成后运行完整测试
性能下降 🟢 低 🟡 中 插件延迟加载作为可选优化
第三方插件破坏 🟡 中 🔴 高 提供兼容层,保持 Plugin 基类 API 稳定
文档过期 🟡 中 🟡 中 同步更新 README 和 API 文档

10. 时间规划

阶段 预计工作量 依赖
阶段一: 整理模型和异常 0.5 天 -
阶段二: 拆分 protocol 0.5 天 -
阶段三: 实现插件注册表 1 天 -
阶段四: 重构 Device 0.5 天 阶段三
阶段五: 创建 config.py 0.5 天 -
阶段六: 清理 service 模块 0.5 天 阶段一
阶段七: 重命名文件 0.5 天 阶段六
总计 4 天 -

11. 回滚计划

如果重构出现问题:

  1. 分阶段回滚: 每个阶段完成后创建可回滚的 checkpoint
  2. 完整回滚: 使用 git reset 到重构开始前的 commit
  3. 增量回滚: 针对特定问题使用 git revert

12. 后续优化方向 (v2.1+)

本次重构不包含但值得考虑的方向:

  1. 异步上下文管理器: 让 Connection/Response 支持 async with
  2. 连接池: 复用连接,减少连接开销
  3. 配置文件支持: 从配置文件加载参数
  4. 更细粒度的插件: 支持按功能模块禁用不需要的插件
  5. Mock 支持: 提供测试用的 mock device
  6. 类型注解完善: 为插件添加泛型支持

附录

A. 参考资料

B. 术语表

术语 说明
HOST SERVICE ADB Server 提供的服务 (ADBClient 封装)
LOCAL SERVICE Android 设备 adbd 守护进程提供的服务 (Device 封装)
Plugin Device 的功能扩展插件