You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

切换页面时Expo背景音乐无法停止的技术求助

Expo AV 背景音乐无法停止且叠加播放的解决方法

你的问题核心在于背景音乐的停止逻辑不够严谨,同时可能存在组件未触发卸载清理的情况,导致Sound实例没有被正确停止和销毁,重复启动游戏时会创建新的播放实例,造成音频叠加。以下是具体的修复方案:

1. 修复 stopBgMusic 方法,确保完整停止并卸载音频

原方法中直接调用stopAsync()和unloadAsync(),但没有处理音频未加载/已停止的状态,容易导致异步操作失败,音频继续播放。修改后的方法会先检查状态,确保操作有效:

stopBgMusic = async () => {
    try {
        const status = await this.bgMusic.getStatusAsync();
        // 仅对已加载的音频执行停止/卸载操作
        if (status.isLoaded) {
            if (status.isPlaying) {
                await this.bgMusic.stopAsync();
            }
            await this.bgMusic.unloadAsync();
            // 重置Sound实例,避免下次播放复用旧实例引发异常
            this.bgMusic = new Audio.Sound();
        }
    } catch (error) {
        console.log('停止背景音乐出错:', error);
    }
}

2. 优化 loadAndPlay 方法,避免循环音频重复加载

原方法每次调用playBgMusic都会重新加载音频,如果之前的实例未被正确清理,会导致多个播放实例同时运行。修改后的方法会先检查音频是否已加载,避免重复操作:

loadAndPlay = async (audioObject, audioFile, loop = false, volume = 1.0) => {
    try {
        const status = await audioObject.getStatusAsync();
        // 已加载的音频直接播放,跳过重复加载流程
        if (status.isLoaded) {
            if (!status.isPlaying) {
                await audioObject.playAsync();
            }
            return;
        }
        // 未加载时执行加载流程
        await audioObject.loadAsync(audioFile);
        // 等待异步设置完成再播放,确保循环/音量生效
        await audioObject.setIsLoopingAsync(loop);
        await audioObject.setVolumeAsync(volume);
        await audioObject.playAsync()
            .then(async playbackStatus => {
                if (!loop) {
                    setTimeout(() => {
                        audioObject.unloadAsync().catch(err => console.log(err));
                    }, playbackStatus.playableDurationMillis);
                }
            })
            .catch(error => {
                console.log('播放出错:', error);
            })
    } catch (error) {
        console.log('加载音频出错:', error);
    }
}

3. 主动触发停止逻辑,不依赖组件卸载

如果使用React Navigation跳转页面,游戏组件可能不会被立即卸载(栈导航中组件会保留在栈内),此时useEffect的清理函数不会执行。需要在游戏结束的主动逻辑中调用停止方法:

// 示例:游戏结束跳转结算页的函数
const handleGameOver = async () => {
    // 先停止音乐再跳转
    await audioController.stopBgMusic();
    navigation.navigate('ResultScreen');
}

4. 确保AudioController是全局单例

如果每次组件渲染都创建新的AudioController实例,旧实例的音频会继续播放,导致叠加。需在组件外部实例化,保证全局唯一:

// 组件文件顶部
const audioController = new AudioController();

// 组件内部使用
useEffect(() => {
    audioController.playBgMusic();
    return () => {
        audioController.stopBgMusic();
    };
}, []);

内容的提问来源于stack exchange,提问作者Elisabete Costa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 14:06:04