问题描述
当用户离开我的页面时,我无法取消loadAsync。我试图在useEffect上使用清除功能,但是由于soundobject还没有加载,因为soundobject等于null,所以会给我一个错误。当其他页面成为焦点时,我也尝试使用redux并添加soundobject.stopAsync,但是由于soundobject可能尚未设置,但不会取消,并且我会播放音频并且无法停止。这是我的“暂停/播放”按钮组件,在其中调用loadAsync。任何帮助将不胜感激。谢谢
更新至我的游戏暂停处理程序 即使我觉得有更好的方法,我也找到了解决方法。我现在正在调用Audio.setIsEnabledAsync(false);作为清理功能。
//CLEANUP FUNCTION
useEffect(() => {
Audio.setIsEnabledAsync(true);
return function cleanUp() {
reference.putFile(props.audioFile).cancel();
Audio.setIsEnabledAsync(false);
};
},[]);
import React,{ useState,useEffect } from "react";
import { TouchableOpacity } from "react-native";
import { usedispatch,useSelector } from "react-redux";
import storage from "@react-native-firebase/storage";
import { playPause,stopPlay } from "../../../store/actions/playerActions";
import { Audio } from "expo-av";
import SmallIndicator from "../Indicators/SmallIndicator";
import { FontAwesomeIcon } from "@fortawesome/react-native-fontawesome";
import { faPlay,faPause } from "@fortawesome/pro-light-svg-icons";
import Colors from "../../../constants/Colors";
const PlayPause = (props) => {
const dispatch = usedispatch();
// LOAD FROM FIREBASE VARIABLES
let audioFile = props.audioFile;
const reference = storage().ref(audioFile);
let task = reference.getDownloadURL();
//HOOKS
const isPlaying = useSelector((state) => state.player.isPlaying);
const [iconSwitch,setIconSwitch] = useState(faPlay);
const [soundobject,setSoundobject] = useState(null);
const [isLoading,setIsLoading] = useState(false);
// LOAD AUdio SETTINGS
useEffect(() => {
const audioSettings = async () => {
try {
await Audio.setAudioModeAsync({
allowsRecordingIOS: false,interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,playsInSilentModeIOS: true,interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DUCK_OTHERS,shouldDuckAndroid: true,staysActiveInBackground: false,playThroughEarpieceAndroid: true,});
} catch (e) {
console.log(e);
}
audioSettings();
};
},[]);
//CLEANUP FUNCTION
useEffect(() => {
Audio.setIsEnabledAsync(true);
return function cleanUp() {
reference.putFile(props.audioFile).cancel();
Audio.setIsEnabledAsync(false);
};
},[]);
// STOP PLAY ON PAGE EXIT
useEffect(() => {
ifPlaying();
},[isPlaying]);
const ifPlaying = async () => {
if (isPlaying === false && soundobject != null) {
await soundobject.stopAsync();
await soundobject.unloadAsync();
setSoundobject(null);
setIconSwitch(faPlay);
}
};
// PLAY PAUSE TOGGLE
const handlePlayPause = async () => {
setIsLoading(true);
let uri = await task;
//PLAY
if (isPlaying === false && soundobject === null) {
const soundobject = new Audio.sound();
await soundobject.loadAsync({ uri },isPlaying,true);
setSoundobject(soundobject);
soundobject.playAsync();
dispatch(playPause(true));
setIconSwitch(faPause);
// PAUSE
} else if (isPlaying === true && soundobject != null) {
dispatch(playPause(false));
setIconSwitch(faPlay);
// STOP AND PLAY
} else if (isPlaying === true && soundobject === null) {
dispatch(stopPlay(true));
dispatch(playPause(true));
const soundobject = new Audio.sound();
const status = { shouldplay: true };
await soundobject.loadAsync({ uri },status,true);
setSoundobject(soundobject);
soundobject.playAsync();
setIconSwitch(faPause);
// RESUME PLAY
} else if (isPlaying === false && soundobject != null) {
dispatch(playPause(true));
soundobject.playAsync();
setIconSwitch(faPause);
}
setIsLoading(false);
};
console.log(isPlaying);
if (isLoading) {
return <SmallIndicator />;
}
return (
<TouchableOpacity onPress={handlePlayPause}>
<FontAwesomeIcon icon={iconSwitch} size={35} color={Colors.primary} />
</TouchableOpacity>
);
};
export default PlayPause;
PlayPause组件位于我的SongItem组件中,我不会添加不适用的代码。
const SongItem = (props) => {
return (
<View>
<PurchaseModal
visible={modalToggle}
purchaseSelector={purchaseSelector}
radio_props={LicenseData}
onPress={modalToggleHandler}
/>
<View>
<Card>
<BodyText>{props.items.name}</BodyText>
<View style={styles.innerContainer}>
<PlayPause audioFile={props.items.audio} />
<TouchableOpacity onPress={cartPress}>
<FontAwesomeIcon
icon={iconSwitch}
size={35}
color={Colors.primary}
/>
</TouchableOpacity>
</View>
<TouchableOpacity onPress={modalToggleHandler} style={toggleStyle}>
<FontAwesomeIcon
icon={faFileInvoice}
size={35}
color={Colors.primary}
/>
</TouchableOpacity>
</Card>
</View>
</View>
);
};
SongItem位于我的SongScreen上。当我调用dispatch(stopPlay)时,我将isPlaying切换为false;
const SongScreen = (props) => {
const filteredSongs = useSelector((state) => state.filter.filteredSongs);
const { goBack } = props.navigation;
const dispatch = usedispatch();
const backPress = () => {
dispatch(stopPlay());
goBack();
};
useEffect(() => {
props.navigation.addListener("didBlur",() => {
dispatch(stopPlay());
});
});
return (
<Gradient>
<FlatList
removeClippedSubviews={false}
windowSize={2}
maxToRenderPerBatch={6}
data={filteredSongs}
keyExtractor={(item) => item.id.toString()}
renderItem={(itemData) => <SongItem items={itemData.item} />}
/>
<MainButton name={"Back"} onPress={backPress} />
</Gradient>
);
}
};
解决方法
我一直在努力应对同样的挑战,甚至直接询问expo并检查了其源代码,以发现没有办法取消并已经加载音频。
我所做的就是借助setOnPlaybackStatusUpdate
回调解决了这个问题。
说明:如果我想先取消一首歌曲,我必须等到它加载完毕之后,借助setOnPlaybackStatusUpdate
,可以在完成加载后立即停止并卸载音频。
所以您对我的stopPlay
函数将如下所示:
try{
await audio.stopAsync();
audio.setOnPlaybackStatusUpdate(null);
}catch(e){
//Error thrown if audio is still loading
//Wait until it has finished loading and stop it
const stopListener = new StopListener();
audio.setOnPlaybackStatusUpdate(stopListener.getListener())
}
StopListener看起来像这样
class StopListener {
getListener = () => async (status) => {
const audio = ... //Get the loading audio object here
audio.setOnPlaybackStatusUpdate(null);
await audio.stopAsync();
}
}
如果找到其他解决方案,请分享。
,好像您没有正确使用反应生命周期。
// STOP PLAY ON PAGE EXIT
useEffect(() => {
ifPlaying();
},[isPlaying]);
这是不正确的(至少它不执行注释中的内容)。而是说:isPlaying
标志更改后,执行ifPlaying
。
您需要这样的代码来停止异步代码:
// STOP PLAY ON PAGE EXIT
useEffect(() => {
return cleanUp() {
ifPlaying();
}
});
在销毁组件之前,将执行useEffect
中返回的所有内容。