时长:40-50 分钟
7.1 统一接口设计原则
7.1.1 接口抽象
- 目标:对上层(Vue 组件、业务逻辑)暴露 与平台无关 的 API,调用方只需关心“做什么”,不关心“是 Android / iOS / Harmony”。
- 做法:
- 在 DeviceService 中集中暴露统一方法,如
getAllDevices()、takeScreenshot(platform, deviceId, outputPath)、login(platform, phone)。 - 各平台能力由 ADBManager / IOSManager / HDCManager 实现,DeviceService 根据
platform参数转发到对应 Manager。
- 在 DeviceService 中集中暴露统一方法,如
- 好处:
- 组件中只需写
DeviceService.takeScreenshot('android', deviceId)或DeviceService.takeScreenshot('ios', deviceId),无需判断当前是 adb 还是 ios 命令。 - 新增平台时,只需在 DeviceService 中增加分支并实现对应 Manager,组件层改动最小。
- 组件中只需写
7.1.2 平台差异处理
- 平台标识:统一使用字符串
'android' | 'harmony' | 'ios',与 UI 上的 Tab/设备类型一致。 - 参数差异:
- 设备 ID:Android/Harmony 多为 adb/hdc 的 device serial;iOS 为 UDID。对外统一传
deviceId,内部由各 Manager 使用(如 adb 的-s deviceId、ios 的--udid=deviceId)。 - 可选参数:如录屏的
deviceRecordPath、outputPath、recordFileName在 Harmony 有特殊含义,通过可选参数传递,不支持的平台可忽略。
- 设备 ID:Android/Harmony 多为 adb/hdc 的 device serial;iOS 为 UDID。对外统一传
- 能力差异:
- 部分能力仅某平台具备(如 iOS 快捷登录、Android Assist Agent HTTP 代理),通过 平台专属方法 暴露(如
DeviceService.ios.openXiaoyingTestLogin(phone)),或由 DeviceService 提供getAndroidAppList、getIOSAppList等,组件按当前平台调用。
- 部分能力仅某平台具备(如 iOS 快捷登录、Android Assist Agent HTTP 代理),通过 平台专属方法 暴露(如
- 通用能力(截图、录屏、登录)保持 同一套接口,内部按平台分支。
7.1.3 错误码统一
- 不单独定义数字错误码,而是统一用 成功/失败 + 错误信息:
- 成功:
{ success: true, ... },必要时带message、screenshotPath、output等。 - 失败:
{ success: false, error: string },error为可读文案,便于直接展示给用户或打日志。
- 成功:
- 常见错误文案:
- 设备未连接:
未找到 Android / iOS / Harmony 设备 - 工具不可用:
无法访问 Electron API、ADB/HDC/go-ios 不可用 - 操作失败:由各 Manager 在 catch 或业务逻辑中设置
error: error.message或简短描述。
- 设备未连接:
- 扩展:若后续需要错误码(如用于统计或国际化),可在返回对象中增加
errorCode?: string,而不改变现有success+error的约定。
7.1.4 返回值格式统一
- 设备列表:
Promise<Array>,每个元素至少包含 id、status(或等效),以及平台相关字段(如 type: 'android'|'harmony'、iOS 的 name、model、version 等)。 DeviceService 的 getAllDevices() 合并三端列表,前端可根据 type 或来源区分。
- 操作类接口(截图、录屏、登录、安装/卸载):
Promise,成功时可有 screenshotPath、output、message 等,失败时必有 error。
- 查询类接口(应用列表、相册列表、照片列表):
成功时返回 { success: true, data: ... } 或直接返回业务数据(如数组),失败时返回 { success: false, error: string } 或抛出,由调用方统一 try/catch 或判断 success。
- 一致性:
各 Manager(adb/ios/hdc)在内部尽量统一“成功即带 success: true + 必要字段,失败即带 success: false + error”,这样 DeviceService 只需透传或简单包装,无需再转换格式。
7.2 DeviceService 设计
7.2.1 平台检测
- isPlatformAvailable(platform):
检测指定平台的 命令行工具 是否可用(如 adb/hdc 是否在 PATH 或项目 commands 目录下、go-ios 是否可用)。 内部调用 this.adb.isAvailable() / this.hdc.isAvailable() / this.ios.isAvailable()。
- hasDevicesForPlatform(platform):
检测指定平台 当前是否有设备连接(如 adb devices 是否有 device、hdc list targets 是否有 online、ios list 是否有设备)。 用于 UI 上显示“当前平台暂无设备”或禁用部分操作。
- getDeviceStatus():
一次性检测 三端 的工具可用性 + 设备列表,并返回汇总结构(如 { android: { available, devices, hasDevices }, harmony: {...}, ios: {...} })。 可用于首页或设置页展示“Android 可用 / 2 台设备”等,实现时可用 Promise.all + 超时,避免某端卡住导致整页慢。
7.2.2 设备管理
- getAllDevices():
并行调用 adb.getDevices()、hdc.getDevices()、ios.getDevices(),将三个数组合并后返回。 前端可根据 device.type 或设备来源区分平台,用于统一设备列表或分 Tab 展示。
- getAllDevicesInfo(options):
并行获取三端设备的 详细信息(型号、系统版本、电量等),返回 { android: [], harmony: [], ios: [] }。 可用于设备详情卡片,实现时可用 Promise.allSettled 避免单端超时导致整批失败。
- 设备对象结构:
- Android:
{ id, status: 'device', type: 'android' },后续可扩展model、version等。 - Harmony:
{ id, status: 'online', type: 'harmony' }。 - iOS:
{ id, name, platform, brand, model, version, status: 'connected' }等。
- Android:
统一保留 id 作为设备唯一标识,供后续截图、录屏、登录等接口使用。
7.2.3 统一登录接口
- login(platform, phone):
根据 platform 调用 adb.login(phone)、hdc.login(phone) 或 ios.openXiaoyingTestLogin(phone)。 各端实现不同(Android/Harmony 可能走 shell 或 Deep Link,iOS 走快捷登录),但对调用方统一为“平台 + 手机号”。
- 返回值:
统一为 { success: boolean, error?: string, ... },成功时可带 message 或输出信息,失败时必带 error。 前端可根据 success 提示“登录已发起”或“登录失败:xxx”。
7.2.4 截图/录屏接口
- takeScreenshot(platform, deviceId, outputPath):
根据 platform 调用对应 Manager 的 takeScreenshot(deviceId, outputPath)。 输出路径可由各 Manager 内部使用 getScreenshotBaseDir() 等 IPC 获取可写目录,若调用方未传则使用默认路径。
- startScreenRecord(platform, deviceId, outputPath, duration):
同理按平台转发;各端录屏方式不同(Android scrcpy/adb、Harmony hdc 录屏、iOS QuickTime/xrecord 等),对外统一为“开始录屏 + 最长时长”。
- stopScreenRecord(platform, deviceId, ...):
停止录屏;Android/Harmony 可能需要设备端路径、拉取到本地的路径等,通过可选参数传递,iOS 可能只需 deviceId。 返回值统一为 { success, error? },成功时可带 outputPath 等。
7.3 各平台实现对比
7.3.1 iOS vs Android vs Harmony
| 能力 | Android | Harmony | iOS |
| 设备列表 | `adb devices` | `hdc list targets` | `ios list` (go-ios) |
| 设备 ID | 序列号 | 序列号 | 序列号 |
| 截图 | adb shell screencap + pull | dc shell snapshot_display + pull | go-ios screenshot / 镜像 |
| 录屏 | scrcpy 或 adb shell screenrecord | hdc shell 录屏 + 拉取 | QuickTime / xrecord / 原生 |
| 登录 | shell am / Deep Link | shell aa start (Deep Link) | WDA/Companion 打开 URL Scheme |
| 应用列表 | adb shell pm / Assist Agent | hdc shell bm dump | ios apps (go-ios) |
| 相册/照片 | adb pull / DCIM | 文件系统或类似 | Photos 框架 / photo-proxy |
| 当前环境 | fetchUrlOnDevice (Assist Agent) | fetchUrlOnDevice (hdc) | fetchUrlOnDevice (URL Service) |
7.3.2 实现差异分析
- 命令层:
Android/Harmony 以 命令行工具(adb/hdc) 为主,通过 execute(command) 执行;iOS 以 go-ios 和 PhotoCompanion / URL Service 等 TCP 服务为主,部分能力需先建立连接再发请求。
- 依赖:
Android 可选 Assist Agent(HTTP 代理、应用列表等);iOS 依赖 WDA/Companion、开发者镜像(部分版本);Harmony 依赖 HDC 与系统能力。 统一接口层不关心这些细节,只要求“平台可用时返回一致格式”。
- 超时与稳定性:
各端超时时间可不同(如 iOS 截图可能需等待镜像挂载),在各自 Manager 内设置;DeviceService 只做转发,必要时在 getDeviceStatus 等接口上做总超时。
7.3.3 最佳实践
- 单一入口:
业务层尽量只调 DeviceService,避免组件直接引用 DeviceService.adb / .ios / .hdc,除非是平台专属能力(如 iOS 快捷登录、Android 相册)。
- 平台参数从 UI 来:
当前选中的 Tab 或设备对应的 platform 作为参数传入,避免在 DeviceService 内再猜平台。
- 错误信息直接可用:
各 Manager 返回的 error 应尽量是“用户可读”的短句,便于直接 ElNotification.error({ message: result.error })。
- 并行与超时:
getAllDevices、getDeviceStatus、getAllDevicesInfo 等用 Promise.all / allSettled + 单端超时,避免某端卡住拖慢整页。
7.4 扩展性设计
7.4.1 新平台接入
- 步骤:
- 新增 XXXManager(如
TizenManager),实现与 adb/ios/hdc 同名的能力子集:getDevices()、isAvailable()、hasDevices(),以及takeScreenshot、startScreenRecord、stopScreenRecord、login等(按需)。 - 在 DeviceService 中增加
this.xxx = XxxManager,并在getAllDevices、getDeviceStatus、hasDevicesForPlatform、isPlatformAvailable、login、takeScreenshot、startScreenRecord、stopScreenRecord等方法的分支中增加platform === 'xxx'的处理。 - 前端增加 新平台 Tab 和 设备列表/操作面板,调用现有 DeviceService 接口并传入新 platform 即可。
- 设备对象:
新平台设备建议至少包含 id、status、type: 'xxx',以便与现有三端设备合并展示或过滤。
- 命令路径:
若新平台也依赖命令行工具,可复用 CommandDetector 的扩展方式(在 commands 中增加命令名与路径),或单独维护一份配置。
7.4.2 新功能扩展
- 通用能力:
若新功能三端都支持(如“获取设备电量”),可在各 Manager 上增加同一语义的方法(如 getBatteryLevel(deviceId)),再在 DeviceService 中增加 getBatteryLevel(platform, deviceId) 转发。 返回值仍建议统一为 { success, error?, data? } 或约定好的结构。
- 平台专属能力:
若仅某平台支持(如“iOS 相册列表”),可保留在对应 Manager 上,由 DeviceService 暴露为 getIOSPhotoList 或通过 DeviceService.ios.xxx() 调用;组件按当前平台判断后调用,避免在 DeviceService 里为不支持的平台写空实现。
- 可选能力:
某些能力可能“部分平台有、部分平台无”,可在 DeviceService 中提供 能力检测(如 supportsFeature(platform, featureName)),或约定“不支持时返回 { success: false, error: '当前平台不支持该功能' }”,由 UI 决定是否隐藏或禁用该功能。
7.4.3 插件机制
- 当前形态:
未使用独立“插件”包,而是通过 DeviceService + 多 Manager 在源码内扩展;CommandDetector 负责统一管理命令路径,可视为一种“可配置扩展”。
- 若引入插件机制:
- 注册表:
维护 platform -> Manager 实例 的映射,新平台通过“注册”加入,而不是在 DeviceService 里写死 if/else。
- 接口契约:
定义 IPlatformManager 或最小接口(如 getDevices、isAvailable、takeScreenshot),插件实现该接口并注册到 DeviceService。
- 生命周期:
插件可在应用启动时加载(如读取某目录下的 js 或配置),或在设置页“启用/禁用”某平台,DeviceService 只调用已启用平台的 Manager。
- 收益:
新平台可以独立仓库或 npm 包形式提供,主应用只依赖 DeviceService 的抽象与注册表,无需改主仓库代码即可接入新平台。
小结
- 统一接口:通过 DeviceService 抽象“平台 + 操作”,统一返回值格式(success/error)与设备列表结构(id、status、type 等),便于前端与多端能力复用。
- 平台差异:在各自 Manager 内消化,通过可选参数与平台专属方法暴露差异能力,避免接口膨胀。
- 扩展性:新平台按“新 Manager + DeviceService 分支”接入;新功能按“通用则加转发、专属则挂 Manager”;若需更强扩展可引入插件注册与契约。







Comments | NOTHING