1

I'm encountering an issue with background audio playback in my Expo app using the Expo AV library. The audio playback works fine when the app is in the foreground, but it stops abruptly when the app goes into the background. I've tried setting the staysActiveInBackground option to true during audio initialization, and I've also followed the recommended steps for configuring background audio playback in the app.json file. Despite these efforts, the audio playback still ceases when the app loses focus.

I'm using the AudioPlayer component from the Expo AV library to manage audio playback, and I've ensured that the component is correctly initialized with the audio source URI. However, the issue persists regardless of whether I test the app on iOS or Android devices.

Could someone please provide guidance on how to ensure continuous background audio playback in my Expo app?

here is code of my component Audioplayer:

import React, { useState, useEffect } from "react";
import { StyleSheet, Text, View, TouchableOpacity } from "react-native";
import { Audio } from "expo-av";
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
import Slider from "@react-native-community/slider";

export default function App({ uri, stopAudio }) {
  const [audio, setAudio] = useState(null); // audio object
  const [isPlaying, setIsPlaying] = useState(false); // isPlaying state variable to check if audio is playing
  const [position, setPosition] = useState(0); // position state variable to set position of audio
  const [duration, setDuration] = useState(0); // duration state variable to set duration of audio

  useEffect(() => {
    const loadAudio = async () => {
      if (uri) {
        const { sound, status } = await Audio.Sound.createAsync(
          { uri }, // source
          { shouldPlay: true, staysActiveInBackground: true } // initial status: auto play the audio and stay active in background
        );
        setAudio(sound); // set audio object
        setDuration(status.durationMillis); // set duration
        setPosition(0); // reset position
        setIsPlaying(true); // set isPlaying to true
      }
    };

    loadAudio(); // load the audio when the component mounts

    return () => {
      if (audio) {
        setIsPlaying(false); // set isPlaying to false
      }
    };
  }, [uri]);

  useEffect(() => {
    if (audio) {
      // if audio is set then set isPlaying to true
      const updatePosition = setInterval(async () => {
        const currentPosition = await audio.getStatusAsync(); // get current position of audio
        setPosition(currentPosition.positionMillis); // set position state variable
      }, 1000);

      return () => clearInterval(updatePosition); // clear interval to avoid memory leaks
    }
  }, [audio]);

  const setAudioMode = async () => {
    if (audio) {
      await audio.setAudioModeAsync({ staysActiveInBackground: true });
    }
  };

  useEffect(() => {
    setAudioMode();
  }, [audio]);

  // Rest of your component code

  useEffect(() => {
    if (audio) {
      if (stopAudio) {
        audio.stopAsync(); // Stop audio if stopAudio is true
        setIsPlaying(false);
        setPosition(0);
      }
    }
  }, [stopAudio]);

  // Rest of your component code

  const handlePlayPause = async () => {
    if (audio) {
      if (isPlaying) {
        // if audio is playing then pause it
        await audio.pauseAsync();
        setIsPlaying(false);
      } else {
        await audio.playAsync(); // if audio is not playing then play it
        setIsPlaying(true);
      }
    }
  };

  const handleSeek = async (value) => {
    if (audio) {
      await audio.setPositionAsync(value); // seek audio to the position
      setPosition(value);
    }
  };

  const handleStop = () => {
    if (audio) {
      audio.stopAsync();
      setIsPlaying(false);
      setPosition(0);
    }
  };

  return (
    <View style={styles.container}>
      {/* // this button will play/pause the audio */}
      <TouchableOpacity onPress={handlePlayPause} style={{ padding: 10 }}>
        {isPlaying ? (
          <Ionicons name="pause" size={24} color="black" />
        ) : (
          <Ionicons name="play" size={24} color="black" />
        )}
      </TouchableOpacity>

      {/* // this is a progress bar of audio */}
      <Slider
        style={styles.slider} // style
        minimumValue={0}
        maximumValue={duration} // maximum value should be duration of audio
        value={position}
        onSlidingComplete={handleSeek} // seek the audio when slider is moved
        disabled={!audio}
      />
      <Text>
        {Math.floor(position / 1000)}s / {Math.floor(duration / 1000)}s
      </Text>
      <TouchableOpacity onPress={handleStop} style={{ padding: 10 }}>
        <MaterialCommunityIcons name="stop" size={24} color="black" />
      </TouchableOpacity>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    flexDirection: "row",
    flexWrap: "wrap",
  },
  slider: {
    width: "50%",
    marginTop: 20,
  },
});

here is app.json file:

{
  "expo": {
    "name": "newtourapp",
    "slug": "newtourapp",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "light",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "assetBundlePatterns": ["**/*"],
    "ios": {
      "supportsTablet": true,
      "infoPlist": {
        "UIBackgroundModes": ["audio"] // This allows audio to play in the background
      }
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      },
      "package": "com.saqibansari.newtourapp"
    },
    "web": {
      "favicon": "./assets/favicon.png"
    },
  }
}

I've tried a couple of approaches to achieve background audio playback in my Expo app:

Setting staysActiveInBackground: I've used the staysActiveInBackground option during audio initialization using the Audio.Sound.createAsync method, setting it to true. My expectation was that this would allow the audio to continue playing when the app is in the background.

app.json Configuration: I've added the necessary configuration in my app.json file to support background audio playback. Specifically, I included "UIBackgroundModes": ["audio"] in the ios configuration section. I expected this configuration to ensure that the audio playback doesn't stop when the app loses focus.

James Z
  • 12,209
  • 10
  • 24
  • 44
Saqib Ali
  • 11
  • 2

0 Answers0