第10期:Android 端 APK 功能分享

发布于 3 天前  12 次阅读


主题:在移动端测试工具中实现「选择本机 APK → 安装到已连接 Android 设备」的完整链路,以及可选的扩展(内部分发、批量安装)。


1. 功能概述

1.1 业务场景

  • 测试同学需要频繁将 APK 安装到真机 进行回归、联调或验收:
  • 本机已有 APK 文件(如 CI 构建产物、内部分发包),希望一键安装到当前连接的设备。
  • 避免手工敲 adb install -r xxx.apk 或拖拽到模拟器。
  • 目标:在工具内通过 选择文件 → 选择设备(可选)→ 安装,完成从本机 APK 到设备安装的闭环。

1.2 能力范围

  • 当前实现
  • 通过系统对话框选择本机 APK 文件。
  • 将所选 APK 安装到当前选中的 Android 设备(或默认第一台)。
  • 安装成功后可选刷新应用列表。
  • 可扩展
  • 从内网/制品库下载 APK 再安装。
  • 多设备批量安装、安装记录与版本管理。

2. 技术实现

2.1 整体流程

用户点击「安装应用」
  → 调用主进程「选择 APK 文件」对话框
  → 用户选择本机 APK,获得 filePath
  → DeviceService.installAndroidApp(filePath, deviceId)
  → adb install -r "" 安装到设备
  → 返回成功/失败,前端提示并可选刷新应用列表

2.2 主进程:选择 APK 文件

  • 职责:弹出系统「打开文件」对话框,限制为 APK 类型,将用户选择的文件路径返回给渲染进程。
  • 实现electron/main.js):
ipcMain.handle('select-apk-file', async () => {
  if (!mainWindow) {
    return { canceled: true, filePath: '' }
  }
  const result = await dialog.showOpenDialog(mainWindow, {
    title: '选择 APK 文件',
    properties: ['openFile'],
    filters: [
      { name: 'Android 应用包 (*.apk)', extensions: ['apk'] },
      { name: '所有文件', extensions: ['*'] }
    ]
  })
  return {
    canceled: result.canceled,
    filePath: result.canceled ? '' : (result.filePaths[0] || '')
  }
})
  • 说明
  • properties: ['openFile'] 表示单选文件。
  • filters 优先展示 .apk,同时保留「所有文件」避免误选其它格式。
  • 取消或未选时返回 canceled: truefilePath: '',由前端判断并直接 return,不发起安装。

2.3 预加载层暴露 API

  • preload.cjs 中暴露:
selectApkFile: () => ipcRenderer.invoke('select-apk-file'),
  • 渲染进程通过 window.electronAPI.selectApkFile() 调用,得到 { canceled, filePath }

2.4 DeviceService:统一安装接口

  • DeviceService.installAndroidApp(apkPath, deviceId)

仅做平台转发,实际安装逻辑在 adb.installApp(apkPath, deviceId)

  • 调用方:Android 设备面板中的「安装应用」按钮,传入当前设备 ID(或空则使用默认设备)。

2.5 ADB 层:installApp 实现

  • 位置src/utils/adb.jsinstallApp(apkPath, deviceId)
  • 要点
  1. 参数apkPath 为本机 APK 绝对路径;deviceId 为空时取 getDevices()[0].id
  2. 设备校验:若无设备,直接返回 { success: false, error: '未找到 Android 设备' }
  3. 路径安全:对路径中的双引号转义(replace(/"/g, '\"')),避免在 shell 中截断。
  4. 命令adb -s install -r ""

-r 表示覆盖安装(保留数据),适合测试场景多次安装同一包。

  1. 超时:安装可能较慢(大包或低性能设备),当前使用 300 秒超时。
  2. 结果判断:根据 result.success 以及 stdout/stderr 中是否包含 success成功 判断安装是否成功;否则将 stderr/stdout 或 error 作为错误信息返回。
const installCmd = `-s ${deviceId} install -r "${safePath}"`
const result = await this.execute(installCmd, { timeout: 300000 })
const output = ((result.stdout || '') + 'n' + (result.stderr || '')).toLowerCase()
if (result.success && (output.includes('success') || output.includes('成功'))) {
  return { success: true, message: '应用安装成功', stdout: result.stdout, stderr: result.stderr }
}
return {
  success: false,
  error: result.error || result.stderr || result.stdout || '安装失败,请检查 APK 是否有效、设备是否已连接'
}

2.6 前端:安装入口与反馈

  • 入口:Android 设备面板中「安装应用」按钮,点击后调用 installApp(deviceId)(如当前设备 ID 或列表第一台)。
  • 流程
  1. 检查 window.electronAPI?.selectApkFile 是否存在,不存在则提示「缺少选择文件能力」。
  2. await window.electronAPI.selectApkFile(),若 canceled || !filePath 则直接 return。
  3. loading.value = true,调用 DeviceService.installAndroidApp(filePath, deviceId)
  4. 根据 result.success 弹出成功/失败通知;成功且当前在「应用」Tab 时可调用 showAppList(currentDeviceId) 刷新应用列表。
  5. finallyloading.value = false
  • 错误提示:失败时用 result.error 或 catch 的 String(error) 作为 Notification 的 message,便于用户排查(如「未找到 Android 设备」「安装失败,请检查 APK 是否有效」)。

3. 错误处理与体验

3.1 常见错误

| 现象 | 可能原因 | 处理建议 | |------|----------|----------| | 选择文件后无反应或安装失败 | 路径含空格/特殊字符导致 shell 解析错误 | 已对双引号转义;若路径含空格,确保外层用双引号包裹(当前 "${safePath}" 已满足) | | 提示「未找到 Android 设备」 | 未连接设备或 adb devices 无 device | 检查 USB 连接、设备是否开启 USB 调试、是否授权本机 | | 安装失败,stderr 含 signature 等 | 已安装版本签名与当前 APK 不一致 | 先卸载旧版本再安装,或使用 install -r -d 等(按需扩展) | | 超时 | APK 体积大或设备慢 | 当前 300s;若仍超时可适当加大或提示用户等待 |

3.2 体验优化

  • 安装中:按钮或区域 loading,避免重复点击。
  • 安装成功:成功提示 + 可选自动刷新应用列表,便于立刻看到新装应用。
  • 安装失败:错误信息直接展示在 Notification 中,必要时在控制台输出完整 stdout/stderr 便于排查。

4. 与 Assist Agent 的区分

  • 本功能:用户主动选择本机任意 APK 并安装到设备,面向「装被测应用、装内部分发包」等场景。
  • Assist Agent:工具内置android-assist-agent/build/app-release.apk,用于应用列表、HTTP 代理等能力;由 ensureAssistAgentInstalled() 在设备列表就绪后按需静默安装,无需用户选择文件。
  • 两套逻辑独立:用户安装 APK 走 selectApkFile + installAndroidApp;Assist Agent 走 ensureAssistAgentInstalled + 固定路径 APK。

5. 扩展方向(可选)

  • 从 URL 安装

增加「输入 APK 下载地址」或从内网制品库选择版本,主进程或渲染进程下载到临时目录后调用 installAndroidApp(tempPath, deviceId),安装完成后删除临时文件。

  • 多设备批量安装

用户选择一台或多台设备,对同一 APK 路径循环调用 installAndroidApp(apkPath, deviceId),汇总成功/失败列表并提示。

  • 安装记录

将每次安装的包名、版本、设备 ID、时间记录到本地或上报,便于统计或回溯。


小结

  • Android 端 APK 功能通过 主进程文件选择对话框 + DeviceService.installAndroidApp + adb install -r 实现「选文件 → 装到设备」的闭环。
  • 路径转义、超时、成功判定与错误信息统一在 adb 层处理,前端只负责调用与提示;与 Assist Agent 的自动安装相互独立,便于维护和扩展。

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