You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React Native音频API真机播放后无法录音,仅模拟器正常

React Native音频API真机播放后无法录音,仅模拟器正常

我之前在iOS真机上碰到过一模一样的问题,这个-10851错误简直是噩梦!本质是iOS的音频会话冲突——播放音频后,音频资源没有被正确释放,或者录音前的音频会话配置和播放时的配置冲突,导致录音的AVAudioEngine无法初始化。模拟器因为音频栈的宽松性不会触发这个问题,但真机上卡得死死的。

先拆解错误原因

日志里的AURemoteIO.cpp:1091 failed: -108510 Hz是核心线索:

  • -10851是iOS音频服务的「资源不可用」错误,通常是音频会话的类别、配置和要启动的音频单元(播放/录音)不匹配。
  • 0 Hz说明系统没有正确获取到录音的采样率配置,和播放时的音频格式直接冲突了。

针对性解决方案(亲测有效)

1. 全局统一管理音频会话(最关键!)

iOS的AVAudioSession是全局单例,播放和录音必须切换正确的会话类别,不能各自为政。推荐用react-native-audio-session库来管理,不用自己写原生模块:

首先安装依赖:

npm install react-native-audio-session
# 或 yarn add react-native-audio-session

然后封装两个会话配置函数,播放前调用播放配置,录音前调用录音配置:

import AudioSession from 'react-native-audio-session';

// 配置播放用的音频会话
export async function setupPlaybackSession() {
  try {
    // 先停用当前会话,彻底避免冲突
    await AudioSession.setActive(false);
    // 设置播放类别,支持后台播放(按需调整)
    await AudioSession.setCategory(AudioSession.Category.Playback, {
      interruptionModeIOS: AudioSession.InterruptionModeIOS.DoNotMix,
    });
    await AudioSession.setActive(true);
  } catch (err) {
    console.error("播放会话配置失败:", err);
  }
}

// 配置录音用的音频会话(适配16kHz单通道需求)
export async function setupRecordingSession() {
  try {
    // 先停用播放会话
    await AudioSession.setActive(false);
    // 用PlayAndRecord类别,支持边录边播(不需要的话可以用Record)
    await AudioSession.setCategory(AudioSession.Category.PlayAndRecord, {
      allowsBluetooth: false, // 按需调整
      playThroughSpeaker: false, // 录音时关闭扬声器,用麦克风
      interruptionModeIOS: AudioSession.InterruptionModeIOS.DoNotMix,
      // 强制指定16kHz采样率,解决日志里的0Hz问题
      sampleRateIOS: 16000,
      numberOfChannelsIOS: 1,
    });
    await AudioSession.setActive(true);
  } catch (err) {
    console.error("录音会话配置失败:", err);
  }
}

2. 播放后强制释放所有资源

播放结束后,不能只调用stop(),必须销毁播放实例并重置会话:

// 假设你用react-native-sound播放音频
import Sound from 'react-native-sound';

let audioPlayer = null;

async function playAudio(path) {
  await setupPlaybackSession();
  audioPlayer = new Sound(path, '', (err) => {
    if (err) {
      console.error('加载音频失败:', err);
      return;
    }
    audioPlayer.play(() => {
      // 播放结束后立即清理
      cleanupPlayer();
    });
  });
}

async function cleanupPlayer() {
  if (audioPlayer) {
    audioPlayer.stop();
    audioPlayer.release(); // 关键!释放底层AVAudioPlayer原生资源
    audioPlayer = null; // 让GC回收实例
    await AudioSession.setActive(false); // 停用播放会话
  }
}

3. 录音前严格校验会话状态

启动录音前,先调用setupRecordingSession(),并且确保会话激活成功后再初始化录音引擎:

// 假设用react-native-audio录制16kHz WAV
import AudioRecorderPlayer from 'react-native-audio-recorder-player';

const audioRecorderPlayer = new AudioRecorderPlayer();

async function startRecording() {
  await setupRecordingSession();
  // 明确指定录音格式为16kHz单通道WAV
  const recordingOptions = {
    ios: {
      audioQuality: 'low',
      sampleRate: 16000,
      numberOfChannels: 1,
      audioEncoding: 'wav',
    },
  };
  try {
    const path = await audioRecorderPlayer.startRecorder('', recordingOptions);
    console.log('开始录音:', path);
  } catch (err) {
    console.error('启动录音失败:', err);
  }
}

4. 避免多音频引擎实例冲突

如果你同时用了播放和录音的独立引擎(比如AVAudioEngine),一定要确保播放引擎完全停止并销毁后再启动录音引擎:

// 播放引擎清理示例(原生或RN封装的引擎都适用)
async function stopAndDestroyPlaybackEngine() {
  if (playbackEngine) {
    playbackEngine.stop();
    playbackEngine = null; // 彻底销毁引擎实例
  }
}

最后检查两个细节

  • Info.plist权限:确保添加了麦克风和音频使用权限,否则真机可能悄悄失败:
    <key>NSMicrophoneUsageDescription</key>
    <string>需要访问麦克风进行录音</string>
    <key>NSMusicUsageDescription</key>
    <string>需要访问音频播放功能</string>
    
  • 会话激活重试:某些场景下会话激活可能失败,加个重试逻辑更稳妥:
    async function activateSession() {
      try {
        await AudioSession.setActive(true);
      } catch (err) {
        // 重试一次,解决偶发的激活失败
        setTimeout(async () => await AudioSession.setActive(true), 100);
      }
    }
    

这样调整后,真机上播放和录音的切换应该就正常了,不用再重启APP。我当时就是靠统一管理音频会话和严格释放资源解决的这个问题!

火山引擎 最新活动