Ai_GirlFriend/xuniYou/Android设备录音兼容性问题.md
2026-03-02 18:57:11 +08:00

6.8 KiB
Raw Permalink Blame History

Android 设备录音兼容性问题

🐛 问题描述

在某些 Android 设备上,recorderManager.onStop 回调中的 res 对象缺少 durationfileSize 字段:

{
    "tempFilePath": "_doc/uniapp_temp_xxx/recorder/xxx.pcm"
    // duration: undefined  ❌
    // fileSize: undefined  ❌
}

但是文件实际上已经生成了,只是这两个字段没有返回。

🔍 根本原因

这是 uni-app 在某些 Android 设备上的已知 bug

  • 录音文件确实生成了
  • 但是 durationfileSize 字段没有正确返回
  • 特别是使用 PCM 格式时更容易出现

解决方案

修复前的代码(会失败)

recorderManager.onStop((res) => {
    if (!res.duration || res.duration < 500) {
        // ❌ 在某些设备上 res.duration 是 undefined
        // 导致这里总是返回,无法继续
        console.error('录音时长太短')
        return
    }
    
    // 永远不会执行到这里
    fs.readFile({ filePath: res.tempFilePath, ... })
})

修复后的代码(兼容)

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 正常(跳过检查)

🔧 完整的修复代码

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.durationres.fileSize

    • 这两个字段在某些设备上可能为 undefined
    • 只检查 res.tempFilePath 是否存在
  2. 从文件内容获取实际大小

    • 读取文件后,使用 fileRes.data.byteLength 获取实际大小
    • 计算录音时长:byteLength / 32000PCM 16kHz 单声道)
  3. 添加文件大小验证

    • 读取文件后检查大小
    • 如果太小(< 32000 bytes即 < 1 秒),提示用户重试

最佳实践

// ✅ 好的做法
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.durationres.fileSize 为 undefined也能

  1. 正确读取录音文件
  2. 获取实际文件大小
  3. 计算录音时长
  4. 分片发送音频数据
  5. 完成整个对话流程

问题: res.duration 和 res.fileSize 为 undefined
原因: uni-app 在某些 Android 设备上的已知 bug
解决: 跳过这些字段的检查,直接读取文件并从文件内容获取实际大小
状态: 已修复