autool/DroidBot/PLATFORM_GUIDE.md
2026-06-17 19:44:18 +08:00

6.9 KiB

DroidBot 多平台适配指南

本文档介绍 DroidBot 的多平台架构设计和接口标准,帮助开发者扩展新平台支持。

架构概述

DroidBot 采用抽象工厂模式,将平台无关的核心逻辑与平台特定实现分离:

┌──────────────────────────────────────────────────────────┐
│                    DroidBot Core                         │
│  (input_policy.py, input_manager.py, utg.py)            │
└─────────────────────────┬────────────────────────────────┘
                          │ 调用抽象接口
┌─────────────────────────▼────────────────────────────────┐
│                  core/ 抽象层                            │
│  AbstractDevice, AbstractDeviceState, AbstractInputEvent │
│  PlatformFactory                                         │
└─────────────────────────┬────────────────────────────────┘
                          │ 平台实现
┌─────────────────────────▼────────────────────────────────┐
│               platforms/                                 │
│  ├── android/     ← AndroidDevice, AndroidDeviceState    │
│  ├── ios/         ← (预留)                               │
│  └── windows/     ← (预留)                               │
└──────────────────────────────────────────────────────────┘

核心接口

1. AbstractDevice

设备抽象基类,定义平台无关的设备操作接口。

class AbstractDevice(ABC):
    # === 必须实现的方法 ===
    @abstractmethod
    def set_up(self) -> None: ...
    @abstractmethod
    def connect(self) -> bool: ...
    @abstractmethod
    def disconnect(self) -> None: ...
    @abstractmethod
    def tear_down(self) -> None: ...
    @abstractmethod
    def check_connectivity(self) -> bool: ...
    
    @abstractmethod
    def get_current_state(self) -> 'AbstractDeviceState': ...
    @abstractmethod
    def get_display_info(self, refresh: bool = False) -> Dict[str, Any]: ...
    
    @abstractmethod
    def send_event(self, event: 'AbstractInputEvent') -> bool: ...
    @abstractmethod
    def take_screenshot(self, path: str) -> bool: ...
    @abstractmethod
    def unlock(self) -> None: ...
    
    @abstractmethod
    def is_foreground(self) -> bool: ...
    @abstractmethod
    def start_app(self) -> bool: ...
    @abstractmethod
    def pull_back_to_app(self) -> bool: ...
    
    @property
    @abstractmethod
    def app_identifier(self) -> str: ...
    @abstractmethod
    def get_platform_name(self) -> str: ...
    @abstractmethod
    def get_device_info(self) -> Dict[str, Any]: ...
    @abstractmethod
    def create_event_from_dict(self, event_dict: Dict) -> 'AbstractInputEvent': ...

2. AbstractDeviceState

设备状态抽象基类,定义 UI 状态信息接口。

class AbstractDeviceState(ABC):
    # === 必须实现的属性 ===
    @property
    @abstractmethod
    def views(self) -> List[Dict[str, Any]]: ...
    
    @property
    @abstractmethod
    def state_str(self) -> str: ...
    
    @property
    @abstractmethod
    def foreground_page(self) -> Optional[str]: ...
    
    # === 必须实现的方法 ===
    @abstractmethod
    def get_possible_input(self) -> List['AbstractInputEvent']: ...
    @abstractmethod
    def to_dict(self) -> Dict[str, Any]: ...
    @abstractmethod
    def get_text_representation(self, merge_buttons: bool = False) -> tuple: ...

3. AbstractInputEvent

输入事件抽象基类。

class AbstractInputEvent(ABC):
    @abstractmethod
    def send(self, device: 'AbstractDevice') -> bool: ...
    @abstractmethod
    def to_dict(self) -> Dict[str, Any]: ...
    @abstractmethod
    def get_event_str(self, state: Optional['AbstractDeviceState'] = None) -> str: ...

统一视图数据结构

各平台需将 UI 元素转换为统一格式:

{
    # 必需字段
    "temp_id": int,                          # 临时 ID
    "bounds": [[int, int], [int, int]],      # 边界框 [[left,top], [right,bottom]]
    "class": str,                            # 元素类型
    
    # 文本相关
    "text": Optional[str],                   # 显示文本
    "content_description": Optional[str],   # 无障碍描述
    
    # 层级关系
    "parent": Optional[int],                # 父节点 ID
    "children": List[int],                  # 子节点 ID 列表
    
    # 交互属性
    "enabled": bool,
    "visible": bool,
    "clickable": bool,
    "scrollable": bool,
    "editable": bool,
    "checkable": bool,
    "long_clickable": bool,
    
    # 平台特有
    "resource_id": Optional[str]            # 平台元素标识
}

平台映射表

抽象接口 Android iOS Windows
foreground_page package/activity bundleId/viewController exe_path/window_title
app_identifier package_name bundleId exe_path/window_class
resource_id resource_id accessibilityIdentifier AutomationId

添加新平台步骤

1. 创建平台目录

mkdir -p droidbot/platforms/ios

2. 实现核心类

# platforms/ios/__init__.py
from .ios_device import IOSDevice
from .ios_device_state import IOSDeviceState
from .ios_input_event import IOSTouchEvent, IOSKeyEvent, ...

def register_ios_platform():
    from ...core.platform_factory import PlatformFactory, Platform
    
    event_classes = {
        'touch': IOSTouchEvent,
        'key': IOSKeyEvent,
        # ...
    }
    
    PlatformFactory.register_platform(
        Platform.IOS,
        IOSDevice,
        IOSDeviceState,
        event_classes
    )

# 模块导入时自动注册
register_ios_platform()

3. 添加平台枚举

# core/platform_factory.py
class Platform(Enum):
    ANDROID = "android"
    IOS = "ios"          # 新增
    WINDOWS = "windows"  # 新增

4. 在 platforms/init.py 导入

from .ios import IOSDevice, IOSDeviceState

事件类型

所有平台必须支持的事件类型:

事件类型 说明 工厂键名
Touch 点击 touch
LongTouch 长按 long_touch
Swipe 滑动 swipe
Scroll 滚动 scroll
SetText 输入文本 set_text
Key 按键 key
KillApp 终止应用 kill_app