English version: README_EN.md
📱 移动端硬件截图捕获 + 💻 桌面端像素级对比 + 🔗 零配置网络发现
CrossShot 面向移动端 UI 自动化测试场景:Android 前台服务实时监听系统截图事件。移动端通过扫描桌面端展示的配对二维码(QR)或手动输入桌面地址/端口完成配对,上传原始图片并在桌面侧完成像素级 diff、标注和批量管理,帮助测试团队快速判断 UI 回归风险。
-
移动端 (Flutter)
- Android 前台服务监听 MediaStore 截图 & 浮窗按钮手动触发
- 二维码(QR)配对:移动端扫描桌面端展示的 QR,先通过
GET /health验证可达性及 token,然后调用POST /api/announce完成配对并开始心跳(/api/heartbeat)。iOS 端默认不再使用自动局域网发现机制,改用二维码配对。 - 多重重试+PNG 重新编码,确保系统截图写入完成后再上传
- Dio 上传 + 权限、网络状态自动处理
-
桌面端 (Electron + React)
- Bonjour 广播 + Express HTTP 服务 (默认 18733)
- Multer 管理
userData/screenshots目录上的原图存储 - Pixelmatch + pngjs 生成差异图、统计差异比 & 像素
- React Renderer 通过 IPC 流化读取截图,规避
file://访问限制 - 批量选择、删除、时间轴视图
-
通信链路(配对说明)
-
配对方式:二维码(QR)配对。移动端扫描桌面端展示的 QR,先通过
GET /health验证可达性与 token,成功后调用POST /api/announce完成配对并开始心跳(/api/heartbeat)。iOS 默认不使用自动局域网发现机制,改用二维码配对。- HTTP REST 上传/列表/删除接口
- 跨平台零配置:同网段即可互通
CrossShot/
├── electron_app/ # Electron + React 桌面端
│ ├── src/
│ │ ├── index.ts # 主进程:HTTP、IPC、文件管理(含配对二维码生成)
│ │ ├── preload.ts # Renderer Bridge
│ │ └── renderer/ # React UI (截图列表、对比面板)
│ └── package.json
├── flutter_app/ # Flutter 移动端
│ ├── lib/
│ │ ├── services/ # 截图监控、上传逻辑(含二维码配对/发现逻辑)
│ │ ├── screens/ # 连接、设备、上传 UI
│ │ └── widgets/
│ ├── android/ # 前台服务 + 浮窗(Kotlin)
│ └── pubspec.yaml
├── shared/ # 协议、类型说明等
└── README.md
| 模块 | 依赖 |
|---|---|
| 桌面端 | Node.js ≥ 18、npm 或 pnpm、Electron Forge |
| 移动端 | Flutter ≥ 3.16、Android Studio、(可选)iOS/macOS |
# 桌面端
cd electron_app
npm install
npm run dev # 启动 Electron + Express(桌面端在配对界面展示 QR)
# 移动端
cd ../flutter_app
flutter pub get
flutter run # Android 需真机以获取系统截图建议保持两端处于同一 Wi-Fi,首次启动允许所需系统权限(本地网络、通知、悬浮窗、媒体库等)。
- 启动桌面端:Electron 主进程会自动启动 HTTP 服务器并监听 IPC;在配对界面会展示配对二维码供移动端扫描。
- 启动移动端:移动端可通过扫描桌面端展示的配对二维码或手动输入服务器地址与端口来发现并连接桌面服务。
- 授权 & 监听:Android 端申请截图/存储/悬浮窗/前台服务权限,后台监听 MediaStore 截图或浮窗按钮触发。
- 上传:检测到新截图 → PNG 重新编码 (确保完整) → 通过
POST /api/upload上传。 - 展示 & 对比:Electron 将新截图写入
userData/screenshots,React 渲染列表、选择任意两张触发 pixelmatch 对比并展示差异图、差异比统计。
┌─────────────┐ QR ┌─────────────┐
│ Flutter App │◄──────►│ Electron │
│ Foreground │ HTTP │ + Express │
│ Service │───────►│ + React │
└─────────────┘ └─────────────┘
▲ ▼
MediaStore 监听 IPC 流式读取、像素对比
- 移动端栈:Flutter · Dio · permission_handler · Kotlin 前台服务 + ContentObserver(含配对二维码处理)。
- 桌面端栈:Electron Forge · React 18 · TypeScript · Express · Multer · Pixelmatch · pngjs。
- 数据流:PNG/Base64 → HTTP 上传 → 本地文件 → IPC → React Data URL。
| 字段 | 类型 | 说明 |
|---|---|---|
screenshot |
File | 图片(PNG/JPEG,最终以 PNG 统一存储) |
deviceInfo |
String | 设备信息字符串 |
timestamp |
String | ISO 时间戳 |
响应示例:
{
"success": true,
"data": {
"id": "1700892345123",
"filename": "screenshot_20231125_101500.png",
"path": "/Users/.../screenshots/...png",
"deviceInfo": "Pixel 7 Pro / Android 14",
"timestamp": "2023-11-25T10:15:00.123Z",
"size": 1048576
}
}返回当前存储的截图元数据数组。
删除对应文件及元数据,返回 { "success": true }。
- Flutter:
flutter run热重载、flutter logs查看服务日志、flutter clean清缓存。 - Electron:
npm run dev带调试工具、Renderer 日志通过 DevTools、主进程日志在终端。 - 打包:
- 移动端:
flutter build apk/flutter build appbundle - 桌面端:
npm run make或npm run package(Forge 默认配置)
- 移动端:
- 无法发现服务:确认同一网段、桌面端已启动、macOS 允许本地网络、Android 已授予本地网络权限。
- 上传 0 字节或损坏:确保 Android 允许文件读写,等待系统
IS_PENDING=0后再上传(已内置重试,仍失败请查看移动端日志)。 - Electron 无法展示图片:Renderer 已通过 IPC 获取 Data URL,如仍失败请检查
userData/screenshots是否存在文件以及 IPC 权限。 - 对比结果异常:确保两张截图分辨率一致,可在设置中调整 Pixelmatch 阈值。
- 欢迎通过 Issue、Pull Request 提交功能需求或缺陷修复。
- 项目采用 MIT License。
Made with ❤️ for mobile QA teams.