我在使用 Expo AV 库的 Expo 应用程序中遇到背景音频播放问题。当应用程序位于前台时,音频播放工作正常,但当应用程序进入后台时,音频播放会突然停止。我尝试在音频初始化期间将staysActiveInBackground 选项设置为true,并且还按照建议的步骤在app.json 文件中配置后台音频播放。尽管做出了这些努力,当应用程序失去焦点时,音频播放仍然停止。
我使用 Expo AV 库中的 AudioPlayer 组件来管理音频播放,并且我已确保使用音频源 URI 正确初始化该组件。但是,无论我是在 iOS 还是 Android 设备上测试该应用程序,问题仍然存在。
有人可以提供有关如何确保我的 Expo 应用程序中连续背景音频播放的指导吗?
这是我的组件 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,
},
});
这里是app.json文件:
{
"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"
},
}
}
我尝试了几种方法来在我的 Expo 应用程序中实现背景音频播放:
设置staysActiveInBackground:我在音频初始化期间使用Audio.Sound.createAsync 方法使用了staysActiveInBackground 选项,并将其设置为true。我的期望是,当应用程序在后台时,这将允许音频继续播放。
app.json 配置:我在 app.json 文件中添加了必要的配置以支持后台音频播放。具体来说,我在 ios 配置部分包含了 "UIBackgroundModes": ["audio"] 。我希望此配置能够确保应用程序失去焦点时音频播放不会停止。
您需要设置所有音频选项才能正常工作。
Audio.setAudioModeAsync({
staysActiveInBackground: true,
playsInSilentModeIOS: true,
interruptionModeIOS: InterruptionModeIOS.DuckOthers, // Change as you like
interruptionModeAndroid: InterruptionModeAndroid.DuckOthers, // Change as you like
shouldDuckAndroid: true,
playThroughEarpieceAndroid: true,
});
在 app.json 中,您可能还需要添加 Android 权限:
permissions: ['WAKE_LOCK'],