guoyu/_已清理文件备份_周六 22512/md/语音测评闪退问题排查.md

408 lines
9.5 KiB
Markdown
Raw Normal View History

2025-12-06 20:11:36 +08:00
# 语音测评闪退问题排查与解决方案
## 🔍 **问题描述**
App运行语音测评时经常闪退尤其是在导入数据时更容易发生。
---
## 📊 **问题分析**
### **可能原因1内存不足**
- **语音模型文件较大**
- vosk-model-small-cn-0.22.zip 约50-100MB
- 首次解压需要大量内存
- 解压时间约30秒
- **导入数据同时运行**
- 如果正在导入大量学员数据
- 同时运行语音识别
- 内存可能不足导致崩溃
### **可能原因2页面切换时回调未停止**
- **回调函数继续执行**
- 页面卸载后,语音识别回调可能继续执行
- 访问已销毁的组件实例导致崩溃
- **状态检查不严格**
- 只检查 `isRecording` 状态
- 没有检查页面是否已卸载
### **可能原因3数据导入错误影响系统**
- **班级不存在**
- 导入的学员数据包含不存在的班级
- 后端返回错误,前端处理不当
- **数据格式错误**
- Excel格式不符合要求
- 必填字段为空
### **可能原因4模型初始化失败**
- **模型文件损坏或缺失**
- **路径解析错误**
- **权限问题**
---
## ✅ **解决方案**
### **方案1优化页面生命周期已修复**
#### **添加页面卸载标记**
```javascript
data() {
return {
// ... 其他数据
pageUnloaded: false // 页面卸载标记
}
}
```
#### **在onUnload中设置标记**
```javascript
onUnload() {
console.log('[Speech] 页面卸载,清理资源')
// 标记页面已卸载,防止回调继续执行
this.pageUnloaded = true
// 停止语音识别
if (this.isRecording) {
this.isRecording = false
try {
if (typeof stopSpeechVoice === 'function') {
stopSpeechVoice()
}
} catch(e) {
console.error('[Speech] 停止识别时出错:', e)
}
}
// 清理定时器
this.stopAutoScroll()
if (this.scrollTimer) {
clearInterval(this.scrollTimer)
this.scrollTimer = null
}
}
```
#### **添加onHide生命周期**
```javascript
onHide() {
console.log('[Speech] 页面隐藏,暂停识别')
// 页面切换到后台时停止识别
if (this.isRecording) {
this.handleStop()
}
}
```
#### **在回调中检查页面状态**
```javascript
startSpeechVoice((res) => {
// 检查页面是否已卸载
if (self.pageUnloaded) {
console.log('[Speech] 页面已卸载,忽略回调')
return
}
// 检查是否仍在录音状态
if (!self.isRecording) {
console.log('[Speech] 已停止录音,忽略回调')
return
}
// ... 处理识别结果
})
```
---
### **方案2修复数据导入已修复**
#### **班级验证**
- ✅ 在导入前验证班级是否存在
- ✅ 班级不存在时,记录失败原因
- ✅ 不中断整个导入流程
#### **错误处理**
- ✅ 导入失败的记录单独显示
- ✅ 提供详细的失败原因
- ✅ 失败详情支持滚动查看
#### **生成测试数据**
- ✅ 使用 `generate_test_data.py` 生成符合要求的测试数据
- ✅ 所有字段自动标准化为系统选项
- ✅ 班级自动映射到系统实际班级
---
### **方案3优化语音模型加载**
#### **延迟加载**
```javascript
onLoad(options) {
// 先加载内容列表
if (options.contentId) {
this.loadContentById(options.contentId)
} else {
this.loadContentList()
}
// 延迟1秒后再初始化模型
setTimeout(() => {
// #ifdef APP-PLUS
this.initSpeechModel()
// #endif
}, 1000)
}
```
#### **检查内存**
```javascript
initSpeechModel() {
// 检查可用内存如果API支持
// #ifdef APP-PLUS
try {
const memInfo = plus.device.getInfo()
console.log('[Speech] 设备内存信息:', memInfo)
} catch(e) {
console.log('[Speech] 无法获取内存信息')
}
// #endif
// ... 继续初始化
}
```
---
### **方案4添加错误捕获**
#### **全局错误处理**
```javascript
// App.vue 或 main.js
uni.onError((error) => {
console.error('[Global] 全局错误:', error)
// 可以记录到后端日志
// 显示友好的错误提示
uni.showToast({
title: '应用出错,请重启',
icon: 'none',
duration: 3000
})
})
```
#### **Try-Catch包裹关键代码**
```javascript
async evaluateScore() {
if (!this.recognizedText || !this.selectedContent) {
uni.showToast({ title: '请先完成语音识别', icon: 'none' })
return
}
this.isEvaluating = true
try {
const result = await evaluateSpeechRecognition(
this.selectedContent.content,
this.recognizedText
)
if (result.code === 200 && result.data) {
this.scoreResult = result.data
await this.saveScoreToBackend(result.data)
uni.showToast({ title: '评分完成', icon: 'success', duration: 2000 })
} else {
throw new Error(result.msg || '评分失败')
}
} catch (error) {
console.error('评分失败', error)
uni.showToast({
title: error.message || '评分失败,请重试',
icon: 'none',
duration: 3000
})
} finally {
this.isEvaluating = false
}
}
```
---
## 🧪 **测试步骤**
### **1. 测试页面切换**
1. 打开语音测评页面
2. 开始语音识别
3. 立即返回首页
4. 观察是否崩溃
**预期结果:** ✅ 不崩溃,识别自动停止
### **2. 测试数据导入**
1. 准备100条测试数据
2. 在语音测评页面
3. 同时执行数据导入
4. 观察是否崩溃
**预期结果:** ✅ 不崩溃,两个操作互不影响
### **3. 测试模型加载**
1. 清除模型缓存
2. 重新打开语音测评
3. 等待模型加载
4. 观察是否崩溃
**预期结果:** ✅ 不崩溃,正常加载
### **4. 测试长时间识别**
1. 开始语音识别
2. 持续朗读3分钟
3. 观察是否崩溃
**预期结果:** ✅ 不崩溃,正常识别
---
## 📋 **使用建议**
### **避免同时操作**
- ❌ 不要在导入数据时使用语音测评
- ❌ 不要在语音识别时导入大量数据
- ✅ 先完成一个操作,再进行另一个
### **分批导入数据**
- ✅ 每次导入不超过50条
- ✅ 等待上一批导入完成再导入下一批
- ✅ 避免一次性导入过多数据
### **定期清理缓存**
- ✅ 在设置中清理应用缓存
- ✅ 清理语音模型缓存后重新加载
- ✅ 卸载重装应用(如果频繁崩溃)
### **检查设备**
- ✅ 确保设备有足够的可用内存至少500MB
- ✅ 关闭其他占用内存的应用
- ✅ 重启设备
---
## 🔧 **调试方法**
### **1. 查看控制台日志**
```bash
# HBuilderX中查看真机运行日志
# 搜索关键字:[Speech]、Error、crash
```
### **2. 查看后端日志**
```bash
# 查看后端日志,是否有错误
tail -f logs/app.log | grep ERROR
```
### **3. 使用测试数据**
```bash
# 生成测试数据
python generate_test_data.py
# 导入测试数据,观察是否正常
```
### **4. 逐步排查**
1. **不使用语音测评** - 只导入数据
- 如果正常:语音测评有问题
- 如果崩溃:数据导入有问题
2. **不导入数据** - 只使用语音测评
- 如果正常:数据导入影响了语音测评
- 如果崩溃:语音测评本身有问题
3. **清除数据** - 卸载重装应用
- 如果正常:缓存数据有问题
- 如果崩溃:代码或资源文件有问题
---
## 💡 **优化建议**
### **1. 按需加载模型**
- 不在onLoad中自动初始化
- 用户点击"开始说话"时才加载
- 显示"模型加载中"提示
### **2. 使用更小的模型**
- 当前使用 vosk-model-small-cn-0.22
- 可以使用更小的模型(如果存在)
- 或者使用在线API需要网络
### **3. 优化数据导入**
- 使用分批导入每批20-30条
- 显示导入进度
- 支持后台导入
### **4. 添加异常恢复**
- 崩溃后自动保存状态
- 重新打开时恢复上次状态
- 提供"恢复上次会话"功能
---
## ✅ **验收标准**
- [ ] 页面快速切换不崩溃
- [ ] 导入数据时不崩溃
- [ ] 长时间语音识别不崩溃
- [ ] 模型加载失败有友好提示
- [ ] 数据导入失败有详细错误信息
- [ ] 控制台无严重错误日志
- [ ] 内存占用在合理范围内
---
## 📝 **修改文件清单**
### **已修改**
1.`fronted_uniapp/pages/speech/speech.vue`
- 添加 `pageUnloaded` 标记
- 添加 `onHide` 生命周期
- 回调中检查页面状态
2.`Study-Vue-redis/ry-study-ui/src/views/study/classUser/index.vue`
- 导入失败对话框添加滚动条
3.`Study-Vue-redis/ry-study-admin/.../StudyClassUserController.java`
- 移除导入前的班级验证
4.`Study-Vue-redis/ry-study-system/.../StudyClassUserServiceImpl.java`
- 添加逐条班级验证
- 记录失败原因到错误信息
### **新增文件**
1.`generate_test_data.py` - 测试数据生成脚本
---
## 🎯 **总结**
### **核心问题**
- 页面卸载后回调继续执行
- 数据导入和语音识别同时进行
- 内存不足
### **解决方法**
- 严格的生命周期管理
- 状态检查
- 分批操作
- 错误处理
### **使用建议**
- 避免同时操作
- 定期清理缓存
- 使用测试数据验证
**现在请测试修复后的版本,观察是否还会闪退!** 🚀