React Native / Firebase / Expo Audio-在下载开始并且用户离开页面后取消loadAsync

问题描述

用户离开我的页面时,我无法取消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中返回的所有内容。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...