9.6 KiB
9.6 KiB
屏幕流监控功能实现方案
一、功能概述
基于 UniApp 开发的安卓 App,实现局域网内后台按需调取学习端屏幕画面的功能。
核心需求
- 按需启动:后台管理端点击特定学生账号后,触发该账号绑定的安卓 App 启动屏幕捕获
- 实时传输:将学习画面实时传输到后台
- 资源节省:不点击时 App 不进行任何捕获和传输操作
- 局域网传输:全程在局域网内传输,不依赖外网
- 权限兼容:适配安卓 10+ 版本,处理好屏幕捕获权限、后台保活
二、技术方案
2.1 架构设计
┌─────────────────┐ WebSocket ┌─────────────────┐
│ 学生端 App │ ◄─────────────────────────► │ 后台管理端 │
│ (UniApp) │ │ (RuoYi) │
│ │ │ │
│ - 屏幕捕获 │ │ - 指令控制 │
│ - WebSocket │ │ - 画面显示 │
│ - 按需启动 │ │ - 学生管理 │
└─────────────────┘ └─────────────────┘
2.2 技术选型
前端(UniApp):
- 屏幕捕获:使用
plus.screen.captureAPI(安卓原生) - 通信协议:WebSocket(实时双向通信)
- 图片编码:Base64(便于 WebSocket 传输)
- 后台保活:使用
FOREGROUND_SERVICE权限
后端(RuoYi):
- WebSocket 服务器:Spring WebSocket
- 控制接口:RESTful API
- 画面转发:WebSocket 消息转发
2.3 数据流程
-
启动监控流程:
后台点击学生 → 调用控制接口 → 发送 WebSocket 指令 → 学生端接收指令 → 开始屏幕捕获 → 传输画面 -
停止监控流程:
后台点击停止 → 调用控制接口 → 发送 WebSocket 指令 → 学生端接收指令 → 停止屏幕捕获 -
画面传输流程:
学生端捕获屏幕 → Base64 编码 → WebSocket 发送 → 后台接收 → 解码显示
三、实现细节
3.1 学生端实现(UniApp)
3.1.1 屏幕流捕获工具类 (screenStream.js)
核心功能:
- WebSocket 连接管理
- 接收后台指令(启动/停止)
- 屏幕捕获和传输
- 自动重连机制
- 后台保活
关键代码:
// 连接 WebSocket
connect() {
const wsUrl = this.getWebSocketUrl()
this.ws = uni.connectSocket({
url: wsUrl,
header: {
'Authorization': `Bearer ${uni.getStorageSync('token') || ''}`
}
})
}
// 开始捕获
startCapture(interval = 500) {
this.isCapturing = true
this.captureTimer = setInterval(() => {
this.captureAndSend()
}, interval)
}
// 捕获并发送
async captureAndSend() {
const screenshotData = await this.captureScreen()
this.sendMessage({
type: 'screen_frame',
userId: this.userId,
data: screenshotData,
timestamp: Date.now()
})
}
3.1.2 权限配置
在 manifest.json 中添加必要权限:
{
"android": {
"permissions": [
"<uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>"
]
}
}
3.1.3 应用初始化
在 App.vue 中初始化屏幕流服务:
onShow() {
const token = uni.getStorageSync('token')
if (token) {
this.initScreenStream()
}
}
3.2 后端实现(RuoYi)
3.2.1 WebSocket 处理器 (ScreenStreamWebSocketHandler.java)
核心功能:
- 管理学生端连接
- 管理监控端连接
- 转发屏幕帧数据
- 发送控制指令
关键代码:
@ServerEndpoint("/ws/screenStream/{userId}")
@Component
public class ScreenStreamWebSocketHandler {
// 学生端连接映射
private static ConcurrentHashMap<String, ScreenStreamWebSocketHandler> studentConnections;
// 监控端连接映射
private static ConcurrentHashMap<String, ScreenStreamWebSocketHandler> monitorConnections;
// 发送指令给学生端
public static void sendCommandToStudent(String studentUserId, String command, Integer interval) {
ScreenStreamWebSocketHandler handler = studentConnections.get(studentUserId);
if (handler != null) {
handler.sendMessage(JSON.toJSONString(cmd));
}
}
}
3.2.2 控制接口 (ScreenStreamController.java)
接口列表:
POST /study/screenStream/start- 启动屏幕流捕获POST /study/screenStream/stop- 停止屏幕流捕获POST /study/screenStream/checkOnline- 检查学生是否在线POST /study/screenStream/onlineCount- 获取在线学生数
3.2.3 安全配置
在 SecurityConfig.java 中添加 WebSocket 路径免认证:
.antMatchers("/ws/**").permitAll()
3.3 后台管理端实现
3.3.1 屏幕流监控页面 (screenStream/index.vue)
功能特性:
- 学生列表展示(带在线状态)
- 点击学生开始/停止监控
- 实时显示屏幕画面
- WebSocket 接收画面数据
关键代码:
// 连接 WebSocket 接收画面
connectWebSocket(studentId) {
const wsUrl = `ws://${host}/ws/screenStream/${studentId}`
this.ws = new WebSocket(wsUrl)
this.ws.onmessage = (event) => {
const message = JSON.parse(event.data)
if (message.type === "screen_frame") {
this.currentScreenFrame = `data:image/jpeg;base64,${message.data}`
}
}
}
// 启动监控
async startMonitor() {
await startScreenStream(this.currentStudent.userId, 500)
this.currentStudent.isMonitoring = true
this.connectWebSocket(this.currentStudent.userId)
}
四、部署说明
4.1 前端部署
-
配置服务器地址:
- 修改
frontend-uniapp/src/utils/config.js中的SERVER_HOST - 确保 WebSocket 地址正确
- 修改
-
编译打包:
cd frontend-uniapp npm run build:app -
权限说明:
- 安卓 10+ 需要用户手动授予屏幕捕获权限
- 首次启动时会请求相关权限
4.2 后端部署
-
确保 WebSocket 支持:
- 检查
pom.xml中已包含spring-boot-starter-websocket依赖
- 检查
-
配置安全策略:
- WebSocket 路径
/ws/**已配置为免认证 - 实际认证在 WebSocket 处理器中处理
- WebSocket 路径
-
启动服务:
cd RuoYi-Vue-redis mvn clean install java -jar ry-news-admin/target/ry-news-admin.jar
五、性能优化
5.1 传输优化
-
图片质量调整:
- 默认质量 0.7(可调整)
- 根据网络情况动态调整
-
捕获频率:
- 默认 500ms(约 2 帧/秒)
- 可根据需求调整
-
数据压缩:
- 使用 JPEG 格式(比 PNG 小)
- Base64 编码传输
5.2 资源管理
-
按需启动:
- 只有后台点击时才启动捕获
- 不监控时完全停止,节省资源
-
连接管理:
- 自动重连机制
- 连接断开时停止捕获
-
后台保活:
- 使用前台服务权限
- 确保应用在后台时也能运行
六、注意事项
6.1 权限问题
-
屏幕捕获权限:
- 安卓 10+ 需要
MediaProjection权限 - 需要用户手动授权
- 安卓 10+ 需要
-
后台运行权限:
- 需要
FOREGROUND_SERVICE权限 - 在
manifest.json中已配置
- 需要
6.2 兼容性
-
安卓版本:
- 最低支持:Android 5.0 (API 21)
- 推荐:Android 10+ (API 29+)
-
网络要求:
- 必须在同一局域网内
- 确保防火墙允许 WebSocket 连接
6.3 安全性
-
认证机制:
- WebSocket 连接时携带 Token
- 后端验证用户身份
-
数据加密:
- 建议在生产环境使用 WSS(WebSocket Secure)
- 局域网环境可考虑使用 HTTP
七、测试建议
7.1 功能测试
-
连接测试:
- 测试学生端 WebSocket 连接
- 测试后台管理端连接
-
指令测试:
- 测试启动/停止指令
- 测试指令响应时间
-
画面传输测试:
- 测试画面清晰度
- 测试传输延迟
- 测试多学生同时监控
7.2 性能测试
-
资源占用:
- 监控 CPU 和内存占用
- 监控网络带宽占用
-
稳定性测试:
- 长时间运行测试
- 网络中断恢复测试
八、后续优化方向
-
WebRTC 方案:
- 如果 WebSocket 方案性能不足,可考虑 WebRTC
- 需要开发原生插件支持
-
流媒体服务器:
- 使用 RTMP/RTSP 协议
- 需要部署流媒体服务器(如 SRS)
-
画面录制:
- 支持录制监控画面
- 保存为视频文件
-
多画面监控:
- 支持同时监控多个学生
- 画面分屏显示
九、常见问题
Q1: 屏幕捕获失败?
A: 检查是否授予屏幕捕获权限,安卓 10+ 需要用户手动授权。
Q2: WebSocket 连接失败?
A: 检查网络连接、防火墙设置,确保在同一局域网内。
Q3: 画面延迟高?
A: 可以调整捕获间隔(默认 500ms),或降低图片质量。
Q4: 应用后台时停止捕获?
A: 检查是否授予后台运行权限,确保 FOREGROUND_SERVICE 权限已配置。
Q5: 多学生同时监控性能问题?
A: 可以限制同时监控的学生数量,或使用流媒体服务器方案。