React Native中Expo-av库页面跳转时无法卸载音频问题求助
解决Expo AV背景音页面跳转后持续播放的问题
你的问题核心在于页面跳转时原组件并未卸载(比如React Navigation栈导航中,旧页面只是被压入栈底,并未销毁),导致原本的useEffect清理函数无法触发。下面是具体的修复方案:
修复步骤
1. 导入导航焦点监听钩子
确保已安装并配置React Navigation,然后导入useFocusEffect:
import { useFocusEffect } from '@react-navigation/native';
2. 替换原useEffect为焦点监听
用useFocusEffect替代普通useEffect,它会在页面获得焦点时执行播放逻辑,失去焦点(跳转至其他页面)时自动执行清理操作:
useFocusEffect( React.useCallback(() => { let soundInstance; // 页面获得焦点时播放背景音 async function playBackgroundSound() { const { sound } = await Audio.Sound.createAsync( require('../assets/audio/BackgroundMusic.mp3') ); soundInstance = sound; await sound.playAsync(); } playBackgroundSound(); // 页面失去焦点时停止并卸载声音 return async () => { if (soundInstance) { await soundInstance.stopAsync(); await soundInstance.unloadAsync(); } }; }, []) );
3. 调整手动播放逻辑(可选)
如果需要保留手动播放按钮,可将播放逻辑抽离,同时确保焦点变化时仍能正确管理声音实例:
const BookSelection = () => { LogBox.ignoreLogs(['new NativeEventEmitter']); let soundInstance = null; async function playSound() { if (soundInstance) { await soundInstance.unloadAsync(); } const { sound } = await Audio.Sound.createAsync( require('../assets/audio/BackgroundMusic.mp3') ); soundInstance = sound; await sound.playAsync(); } useFocusEffect( React.useCallback(() => { return async () => { if (soundInstance) { await soundInstance.stopAsync(); await soundInstance.unloadAsync(); soundInstance = null; } }; }, []) ); // ... 其余代码不变 };
完整修改后的代码示例
import React from 'react'; import { View, ImageBackground, FlatList, Button, StyleSheet } from 'react-native'; import { Audio } from 'expo-av'; import Constants from 'expo-constants'; import { useFocusEffect } from '@react-navigation/native'; const BookSelection = () => { LogBox.ignoreLogs(['new NativeEventEmitter']); useFocusEffect( React.useCallback(() => { let soundInstance; async function playBackgroundSound() { console.log('Loading Sound'); const { sound } = await Audio.Sound.createAsync( require('../assets/audio/BackgroundMusic.mp3') ); soundInstance = sound; console.log('Playing Sound'); await sound.playAsync(); } playBackgroundSound(); return async () => { if (soundInstance) { console.log('Stopping and Unloading Sound'); await soundInstance.stopAsync(); await soundInstance.unloadAsync(); } }; }, []) ); return ( <ImageBackground source={require('../assets/images/book1/page1.png')} style={styles.backgroundImage} > <View style={styles.container}> {/* 如需手动播放可保留按钮,否则移除 */} {/* <Button title="Play Sound" onPress={playSound} /> */} <FlatList data={booksData} renderItem={({ item }) => <Book book={item} />} showsVerticalScrollIndicator={false} /> </View> </ImageBackground> ); }; const styles = StyleSheet.create({ backgroundImage: { flex: 1, resizeMode: 'cover', }, container: { flex: 1, paddingHorizontal: '5%', top: Constants.statusBarHeight, }, }); export default BookSelection;
关键说明
useFocusEffect是React Navigation专为页面焦点变化设计的钩子,比普通useEffect更适配导航场景,能精准捕捉页面显示/隐藏状态。- 清理函数中先执行
stopAsync()再执行unloadAsync(),确保声音完全停止后再释放资源,规避异步操作顺序问题。 - 用
useCallback包裹逻辑,避免组件重渲染时重复创建函数,提升性能。
内容的提问来源于stack exchange,提问作者OTDev
相关产品推荐
相关产品推荐

