Ai_GirlFriend/xuniYou/Android设备录音兼容性问题.md

241 lines
6.8 KiB
Markdown
Raw Normal View History

2026-03-02 18:57:11 +08:00
# Android 设备录音兼容性问题
## 🐛 问题描述
在某些 Android 设备上,`recorderManager.onStop` 回调中的 `res` 对象缺少 `duration``fileSize` 字段:
```javascript
{
"tempFilePath": "_doc/uniapp_temp_xxx/recorder/xxx.pcm"
// duration: undefined ❌
// fileSize: undefined ❌
}
```
但是文件实际上已经生成了,只是这两个字段没有返回。
## 🔍 根本原因
这是 uni-app 在某些 Android 设备上的已知 bug
- 录音文件确实生成了
- 但是 `duration``fileSize` 字段没有正确返回
- 特别是使用 PCM 格式时更容易出现
## ✅ 解决方案
### 修复前的代码(会失败)
```javascript
recorderManager.onStop((res) => {
if (!res.duration || res.duration < 500) {
// ❌ 在某些设备上 res.duration 是 undefined
// 导致这里总是返回,无法继续
console.error('录音时长太短')
return
}
// 永远不会执行到这里
fs.readFile({ filePath: res.tempFilePath, ... })
})
```
### 修复后的代码(兼容)
```javascript
recorderManager.onStop((res) => {
// 只检查文件路径是否存在
if (!res.tempFilePath) {
console.error('没有录音文件路径')
return
}
// ⚠️ 跳过 duration 和 fileSize 的检查
// 因为在某些设备上这些字段可能为 undefined
// 但文件实际上已经生成了
// 直接尝试读取文件
fs.readFile({
filePath: res.tempFilePath,
success: (fileRes) => {
// 文件读取成功后,可以从 fileRes.data 获取实际大小
console.log('实际文件大小:', fileRes.data.byteLength, 'bytes')
// 继续处理...
}
})
})
```
## 📊 兼容性对比
### 修复前
| 设备类型 | duration | fileSize | 结果 |
|---------|----------|----------|------|
| iOS | ✅ 有值 | ✅ 有值 | ✅ 正常 |
| Android (部分) | ✅ 有值 | ✅ 有值 | ✅ 正常 |
| Android (部分) | ❌ undefined | ❌ undefined | ❌ 失败 |
### 修复后
| 设备类型 | duration | fileSize | 结果 |
|---------|----------|----------|------|
| iOS | ✅ 有值 | ✅ 有值 | ✅ 正常 |
| Android (部分) | ✅ 有值 | ✅ 有值 | ✅ 正常 |
| Android (部分) | ❌ undefined | ❌ undefined | ✅ 正常(跳过检查) |
## 🔧 完整的修复代码
```javascript
recorderManager.onStop((res) => {
console.log('⏹️ 录音已停止')
console.log('📋 完整的 res 对象:', JSON.stringify(res))
// 只检查文件路径
if (!res.tempFilePath) {
console.error('❌ 没有录音文件路径!')
uni.showToast({
title: '录音失败:没有生成文件',
icon: 'none'
})
return
}
// ⚠️ 某些 Android 设备上 res.duration 和 res.fileSize 可能为 undefined
// 这是 uni-app 的已知问题,我们跳过这个检查,直接尝试读取文件
if (res.duration !== undefined && res.duration < 500) {
console.error('❌ 录音时长太短:', res.duration, 'ms')
uni.showToast({
title: '录音太短,请至少说 2 秒',
icon: 'none'
})
return
}
console.log('✅ 录音文件路径有效,准备读取文件...')
// 检查 WebSocket 状态
if (!this.socketTask || this.socketTask.readyState !== 1) {
console.error('❌ WebSocket 未连接')
return
}
// 读取文件
const fs = uni.getFileSystemManager()
fs.readFile({
filePath: res.tempFilePath,
success: (fileRes) => {
console.log('✅ 文件读取成功')
console.log('📊 实际文件大小:', fileRes.data.byteLength, 'bytes')
console.log('📊 预计录音时长:', (fileRes.data.byteLength / 32000).toFixed(2), '秒')
// 验证文件大小
if (fileRes.data.byteLength < 32000) {
console.error('❌ 文件太小,可能录音失败')
uni.showToast({
title: '录音文件太小,请重试',
icon: 'none'
})
return
}
// 确保是 ArrayBuffer
if (!(fileRes.data instanceof ArrayBuffer)) {
console.error('❌ 数据不是 ArrayBuffer')
return
}
// 发送音频数据
this.sendAudioInChunks(fileRes.data)
},
fail: (err) => {
console.error('❌ 文件读取失败:', err)
uni.showToast({
title: '文件读取失败',
icon: 'none'
})
}
})
})
```
## 🎓 经验总结
### 关键点
1. **不要依赖 `res.duration` 和 `res.fileSize`**
- 这两个字段在某些设备上可能为 undefined
- 只检查 `res.tempFilePath` 是否存在
2. **从文件内容获取实际大小**
- 读取文件后,使用 `fileRes.data.byteLength` 获取实际大小
- 计算录音时长:`byteLength / 32000` 秒PCM 16kHz 单声道)
3. **添加文件大小验证**
- 读取文件后检查大小
- 如果太小(< 32000 bytes < 1 提示用户重试
### 最佳实践
```javascript
// ✅ 好的做法
if (!res.tempFilePath) {
return // 只检查文件路径
}
// 读取文件后验证
fs.readFile({
success: (fileRes) => {
const size = fileRes.data.byteLength
const duration = size / 32000 // 计算时长
if (duration < 2) {
console.error('录音太短')
return
}
// 继续处理...
}
})
// ❌ 不好的做法
if (!res.duration || res.duration < 500) {
return // 在某些设备上会失败
}
```
## 📱 测试结果
修复后,应该看到:
```
⏹️ 录音已停止
📋 完整的 res 对象: {"tempFilePath":"..."}
📁 文件路径: _doc/uniapp_temp_xxx/recorder/xxx.pcm
⏱️ 录音时长: undefined ms ← 可能是 undefined
📦 文件大小: undefined bytes ← 可能是 undefined
✅ 录音文件路径有效,准备读取文件...
✅ 文件读取成功
📊 实际文件大小: 320000 bytes ← 从文件内容获取
📊 预计录音时长: 10.00 秒 ← 计算得出
📦 开始分片发送(官方推荐参数)
...
```
## 🎉 预期结果
修复后,即使 `res.duration``res.fileSize` 为 undefined也能
1. ✅ 正确读取录音文件
2. ✅ 获取实际文件大小
3. ✅ 计算录音时长
4. ✅ 分片发送音频数据
5. ✅ 完成整个对话流程
---
**问题**: res.duration 和 res.fileSize 为 undefined
**原因**: uni-app 在某些 Android 设备上的已知 bug
**解决**: 跳过这些字段的检查,直接读取文件并从文件内容获取实际大小
**状态**: ✅ 已修复