guoyu/_已清理文件备份_周六 22512/md/语音测评闪退问题排查.md
2025-12-06 20:11:36 +08:00

408 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 语音测评闪退问题排查与解决方案
## 🔍 **问题描述**
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` - 测试数据生成脚本
---
## 🎯 **总结**
### **核心问题**
- 页面卸载后回调继续执行
- 数据导入和语音识别同时进行
- 内存不足
### **解决方法**
- 严格的生命周期管理
- 状态检查
- 分批操作
- 错误处理
### **使用建议**
- 避免同时操作
- 定期清理缓存
- 使用测试数据验证
**现在请测试修复后的版本,观察是否还会闪退!** 🚀