第7期:多平台统一接口设计

发布于 3 天前  10 次阅读


时长: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.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)。
    • 可选参数:如录屏的 deviceRecordPathoutputPathrecordFileName 在 Harmony 有特殊含义,通过可选参数传递,不支持的平台可忽略。
  • 能力差异
    • 部分能力仅某平台具备(如 iOS 快捷登录、Android Assist Agent HTTP 代理),通过 平台专属方法 暴露(如 DeviceService.ios.openXiaoyingTestLogin(phone)),或由 DeviceService 提供 getAndroidAppListgetIOSAppList 等,组件按当前平台调用。
  • 通用能力(截图、录屏、登录)保持 同一套接口,内部按平台分支。

7.1.3 错误码统一

  • 不单独定义数字错误码,而是统一用 成功/失败 + 错误信息
    • 成功:{ success: true, ... },必要时带 messagescreenshotPathoutput 等。
    • 失败:{ success: false, error: string }error 为可读文案,便于直接展示给用户或打日志。
  • 常见错误文案
    • 设备未连接:未找到 Android / iOS / Harmony 设备
    • 工具不可用:无法访问 Electron APIADB/HDC/go-ios 不可用
    • 操作失败:由各 Manager 在 catch 或业务逻辑中设置 error: error.message 或简短描述。
  • 扩展:若后续需要错误码(如用于统计或国际化),可在返回对象中增加 errorCode?: string,而不改变现有 success + error 的约定。

7.1.4 返回值格式统一

  • 设备列表

Promise<Array>,每个元素至少包含 idstatus(或等效),以及平台相关字段(如 type: 'android'|'harmony'、iOS 的 namemodelversion 等)。 DeviceService 的 getAllDevices() 合并三端列表,前端可根据 type 或来源区分。

  • 操作类接口(截图、录屏、登录、安装/卸载)

Promise,成功时可有 screenshotPathoutputmessage 等,失败时必有 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' },后续可扩展 modelversion 等。
    • Harmony:{ id, status: 'online', type: 'harmony' }
    • iOS:{ id, name, platform, brand, model, version, status: 'connected' } 等。

统一保留 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

能力AndroidHarmonyiOS
设备列表`adb devices``hdc list targets``ios list` (go-ios)
设备 ID序列号序列号序列号
截图adb shell screencap + pulldc shell snapshot_display + pullgo-ios screenshot / 镜像
录屏scrcpy 或 adb shell screenrecordhdc shell 录屏 + 拉取QuickTime / xrecord / 原生
登录shell am / Deep Linkshell aa start (Deep Link)WDA/Companion 打开 URL Scheme
应用列表adb shell pm / Assist Agenthdc shell bm dumpios 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-iosPhotoCompanion / 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 })

  • 并行与超时

getAllDevicesgetDeviceStatusgetAllDevicesInfo 等用 Promise.all / allSettled + 单端超时,避免某端卡住拖慢整页。


7.4 扩展性设计

7.4.1 新平台接入

  • 步骤
  1. 新增 XXXManager(如 TizenManager),实现与 adb/ios/hdc 同名的能力子集:getDevices()isAvailable()hasDevices(),以及 takeScreenshotstartScreenRecordstopScreenRecordlogin 等(按需)。
  2. DeviceService 中增加 this.xxx = XxxManager,并在 getAllDevicesgetDeviceStatushasDevicesForPlatformisPlatformAvailablelogintakeScreenshotstartScreenRecordstopScreenRecord 等方法的分支中增加 platform === 'xxx' 的处理。
  3. 前端增加 新平台 Tab设备列表/操作面板,调用现有 DeviceService 接口并传入新 platform 即可。
  • 设备对象

新平台设备建议至少包含 idstatustype: '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 或最小接口(如 getDevicesisAvailabletakeScreenshot),插件实现该接口并注册到 DeviceService。

  • 生命周期

插件可在应用启动时加载(如读取某目录下的 js 或配置),或在设置页“启用/禁用”某平台,DeviceService 只调用已启用平台的 Manager。

  • 收益

新平台可以独立仓库或 npm 包形式提供,主应用只依赖 DeviceService 的抽象与注册表,无需改主仓库代码即可接入新平台。


小结

  • 统一接口:通过 DeviceService 抽象“平台 + 操作”,统一返回值格式(success/error)与设备列表结构(id、status、type 等),便于前端与多端能力复用。
  • 平台差异:在各自 Manager 内消化,通过可选参数与平台专属方法暴露差异能力,避免接口膨胀。
  • 扩展性:新平台按“新 Manager + DeviceService 分支”接入;新功能按“通用则加转发、专属则挂 Manager”;若需更强扩展可引入插件注册与契约。

一名测试工作者,专注接口测试、自动化测试、性能测试、Python技术。