React Native后台定时任务并播放音频的问题解决
React Native后台定时任务无法播放音频问题
我开发了一款React Native应用,要求每15分钟拉取数据并播放音频,无论应用处于前台还是后台。但目前遇到问题:应用进入后台后,15分钟后无法播放音频,仅在重新打开应用时才会播放。以下是我使用的代码:
import React, {useState, useEffect, useCallback} from 'react'; import { SafeAreaView, StyleSheet, ScrollView, View, Text, FlatList, StatusBar, ListRenderItem, } from 'react-native'; import {Header, Colors} from 'react-native/Libraries/NewAppScreen'; import BackgroundFetch from 'react-native-background-fetch'; import {Button} from '@rneui/base'; import Sound from 'react-native-sound'; // Define the type for event interface Event { taskId: string; timestamp: string; } const getTime = () => { let currentDate = new Date(); // Get the current hours, minutes, and seconds let hours = currentDate.getHours(); let minutes = currentDate.getMinutes(); let seconds = currentDate.getSeconds(); // Format the time as HH:MM:SS let currentTime = `${hours.toString().padStart(2, '0')}:${minutes .toString() .padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; return currentTime; }; const App: React.FC = () => { const [events, setEvents] = useState<Event[]>([]); const [data, setData] = useState<any>(getTime()); useEffect(() => { initBackgroundFetch(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const addEvent = useCallback(async (taskId: string): Promise<void> => { console.log('Hello from a background task', getTime()); setEvents(prevEvents => [ ...prevEvents, { taskId: taskId, timestamp: getTime(), }, ]); }, []); const initBackgroundFetch = async () => { const onEvent = async (taskId: string) => { console.log('started task time', getTime()); await addEvent(taskId); BackgroundFetch.finish(taskId); console.log('finished task time', getTime()); triggerBackgroundFetch(); }; const onTimeout = async (taskId: string) => { console.warn('[BackgroundFetch] TIMEOUT task: ', taskId); BackgroundFetch.finish(taskId); }; let status = await BackgroundFetch.configure( { minimumFetchInterval: 15, // <-- minutes (15 is minimum allowed) forceAlarmManager: false, // Set true to bypass JobScheduler. stopOnTerminate: false, startOnBoot: true, }, onEvent, onTimeout, ); console.log('[BackgroundFetch] configure status: ', status); }; const renderItem: ListRenderItem<Event> = ({item}) => ( <Text> [{item.taskId}]: {item.timestamp} </Text> ); const triggerBackgroundFetch = async () => { setData(Math.random() * 50); var whoosh = new Sound('sound.mp3', Sound.MAIN_BUNDLE, error => { if (error) { console.log('failed to load the sound', error); return; } // Play the sound with an onEnd callback whoosh.play(success => { if (success) { console.log('successfully finished playing'); } else { console.log('playback failed due to audio decoding errors'); } }); }); }; return ( <> <StatusBar barStyle="dark-content" /> <SafeAreaView> <ScrollView contentInsetAdjustmentBehavior="automatic" style={styles.scrollView}> <Header /> <View style={styles.body}> <View style={styles.sectionContainer}> <Text style={styles.sectionTitle}>BackgroundFetch Demo</Text> <View> <Text>random:{data}</Text> </View> </View> </View> <Button onPress={triggerBackgroundFetch}>Call API</Button> </ScrollView> <View style={styles.sectionContainer}> <FlatList data={events} renderItem={renderItem} keyExtractor={item => item.timestamp} /> </View> </SafeAreaView> </> ); }; const styles = StyleSheet.create({ scrollView: { backgroundColor: Colors.lighter, }, body: { backgroundColor: Colors.white, }, sectionContainer: { marginTop: 32, paddingHorizontal: 24, }, sectionTitle: { fontSize: 24, fontWeight: '600', color: Colors.black, }, sectionDescription: { marginTop: 8, fontSize: 18, fontWeight: '400', color: Colors.dark, }, }); export default App;
问题原因与解决办法
1. 后台音频权限缺失
iOS和Android系统默认限制后台应用的音频播放能力,必须显式声明权限:
- iOS:在
Info.plist中添加UIBackgroundModes数组,包含audio字段,告诉系统应用需要后台音频权限。 - Android:在
AndroidManifest.xml中添加FOREGROUND_SERVICE权限,同时将音频播放逻辑绑定到前台服务,避免系统因资源回收杀死进程。
2. BackgroundFetch的资源限制
react-native-background-fetch的后台任务执行窗口极短(通常仅数秒),且系统会限制后台任务的资源访问,包括音频硬件。单纯用它触发音频播放不可靠,需要结合专门的后台音频框架。
3. 音频会话未激活
后台状态下,音频会话默认处于暂停状态,需要在播放前强制激活:
- iOS:通过原生
AVAudioSession设置playback类别并主动激活会话。 - Android:使用
AudioManager请求音频焦点,确保播放权限。
4. 代码修改建议
替换为专业后台音频框架
推荐使用react-native-track-player,它原生支持后台音频播放,适配双平台权限:
- 安装依赖:
npm install react-native-track-player
- 按官方文档完成iOS/Android的后台权限配置(包括
Info.plist和AndroidManifest.xml的修改) - 修改播放逻辑:
import TrackPlayer from 'react-native-track-player'; // 初始化播放器 useEffect(() => { TrackPlayer.setupPlayer().then(() => { TrackPlayer.updateOptions({ stopWithApp: false, capabilities: [], compactCapabilities: [], }); }); initBackgroundFetch(); }, []); const triggerBackgroundFetch = async () => { setData(Math.random() * 50); // 重置队列并添加音频播放 await TrackPlayer.reset(); await TrackPlayer.add({ id: 'background-sound', url: require('./sound.mp3'), title: 'Background Audio', }); await TrackPlayer.play(); };
调整BackgroundFetch执行顺序
确保音频播放逻辑在任务结束前完成,避免任务被系统终止:
const onEvent = async (taskId: string) => { console.log('started task time', getTime()); await addEvent(taskId); // 先执行音频播放,再标记任务完成 await triggerBackgroundFetch(); BackgroundFetch.finish(taskId); console.log('finished task time', getTime()); };
内容的提问来源于stack exchange,提问作者Khan Naxim
相关产品推荐
相关产品推荐

