本文档旨在指导开发者如何在 Expo 应用中集成声音和振动通知。我们将探讨如何利用 expo-av 播放声音以及使用 react-native 的 Vibration API 实现振动效果,并着重解决在特定时间触发通知的问题。同时,我们也关注权限处理,这是实现通知功能的关键环节。
集成声音通知
首先,确保你已经安装了 expo-av 依赖:
npx expo install expo-av
以下代码展示了如何播放声音:
import { useEffect, useState } from "react"; import { Audio } from 'expo-av'; import * as Notifications from 'expo-notifications'; import { Platform } from 'react-native'; async function schedulePushNotification() { await Notifications.scheduleNotificationAsync({ content: { title: "Reminder!", body: 'It's time!', sound: 'default', // Use 'default' for the default notification sound data: { data: 'goes here' }, }, trigger: { seconds: 5, repeats: false }, // Schedule for 5 seconds from now }); } const useSound = () => { const [sound, setSound] = useState(null); useEffect(() => { async function loadSound() { try { const { sound: soundObject } = await Audio.Sound.createAsync( require('./assets/notification.mp3'), // 替换为你的音频文件路径 { shouldPlay: false } // 初始时不播放 ); setSound(soundObject); console.log('Sound loaded successfully'); // Set audio mode to allow playing sound in silent mode (ios) await Audio.setAudioModeAsync({ playsInSilentModeIOS: true, staysActiveInBackground: true, interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX, interruptionModeandroid: Audio.INTERRUPTION_MODE_ANDROID_DUCK_OTHERS, shouldDuckAndroid: false, }); } catch (error) { console.error("Failed to load the sound", error); } } loadSound(); return () => { if (sound) { sound.unloadAsync(); } }; }, []); const playSound = async () => { if (sound) { try { await sound.replayAsync(); // Use replayAsync to play from the beginning console.log('Playing Sound'); } catch (error) { console.error("Failed to play the sound", error); } } }; return { playSound }; }; export default function App() { const { playSound } = useSound(); useEffect(() => { // Schedule notification schedulePushNotification(); // Configure notifications async function configurePushNotifications() { const { status } = await Notifications.getPermissionsAsync(); let finalStatus = status; if (finalStatus !== 'granted') { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } if (finalStatus !== 'granted') { alert('Failed to get push token for push notification!'); return; } if (Platform.OS === 'android') { Notifications.setNotificationChannelAsync('default', { name: 'default', importance: Notifications.AndroidImportance.MAX, vibrationPattern: [0, 250, 250, 250], lightColor: '#FF231F7C', }); } } configurePushNotifications(); }, []); useEffect(() => { const subscription = Notifications.addNotificationReceivedListener(notification => { console.log('Notification Received', notification); playSound(); }); const responseListener = Notifications.addNotificationResponseReceivedListener(response => { console.log('Notification Response', response); }); return () => { subscription.remove(); responseListener.remove(); }; }, [playSound]); return ( // Your UI components here null ); }
关键点:
- 使用 Audio.Sound.createAsync 加载音频文件。确保文件路径正确。
- 使用 sound.playAsync() 播放声音。
- 在组件卸载时,使用 sound.unloadAsync() 释放资源。
- Notifications.setNotificationHandler 设置通知处理程序,允许播放声音。
注意: 音频文件需要放置在你的项目中,例如 assets 目录下。你需要替换 require(‘../assets/sonido.mp3’) 为你的实际文件路径。
集成振动通知
要添加振动效果,你需要从 react-native 导入 Vibration API:
import { Vibration } from 'react-native';
然后,你可以使用 Vibration.vibrate() 方法触发振动。你可以传递一个数字(毫秒)来指定振动持续时间,或者传递一个模式数组来定义振动和暂停的序列:
import { Vibration } from 'react-native'; const vibrate = () => { Vibration.vibrate(); // 默认振动 //Vibration.vibrate(1000); // 振动 1 秒 //Vibration.vibrate([0, 500, 200, 500], true); // 自定义模式 (启动, 振动500ms, 暂停200ms, 振动500ms, 循环) };
关键点:
- Vibration.vibrate() 触发振动。
- 可以传递一个数字指定振动时长。
- 可以传递一个数组定义振动模式。
将振动集成到你的通知处理程序中:
import { useEffect } from "react"; import * as Notifications from "expo-notifications"; import { Alert, Vibration } from "react-native"; import React from "react"; import { useNavigation } from "@react-navigation/native"; import { Audio } from 'expo-av'; Notifications.setNotificationHandler({ handleNotification: async () => { return { shouldPlaySound: true, shouldSetBadge: false, shouldShowAlert: true, }; }, }); const HandleNotifications = () => { const navigation = useNavigation(); useEffect(() => { async function configurePushNotifications() { const { status } = await Notifications.getPermissionsAsync(); let finalStatus = status; if (finalStatus !== "granted") { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } if (finalStatus !== "granted") { Alert.alert( "Permiso requerido", "Se requieren notificaciones locales para recibir alertas cuando vencen los recordatorios." ); return; } } configurePushNotifications(); }, []); useEffect(() => { const subscription = Notifications.addNotificationResponseReceivedListener( (response) => { console.log("RESPONSE", response); const reminderId = response.notification.request.content.data.reminderId; if (reminderId) { navigation.navigate("ModifyReminder", { reminderId }); } } ); return () => subscription.remove(); }, []); useEffect(() => { let soundObject = null; async function playSound() { soundObject = new Audio.Sound(); try { await soundObject.loadAsync(require('../assets/sonido.mp3')); await soundObject.playAsync(); } catch (error) { console.log(error); } } playSound(); return () => { if (soundObject) { soundObject.stopAsync(); soundObject.unloadAsync(); } }; }, []); useEffect(() => { const appFocusSubscription = Notifications.addNotificationResponseReceivedListener(() => { Vibration.cancel(); Audio.setIsEnabledAsync(false); }); const appBlurSubscription = Notifications.addNotificationResponseReceivedListener(() => { Audio.setIsEnabledAsync(true); }); return () => { appFocusSubscription.remove(); appBlurSubscription.remove(); }; }, []); useEffect(() => { const subscription = Notifications.addNotificationReceivedListener(() => { Vibration.vibrate([0, 500, 200, 500], true); }); return () => { subscription.remove(); Vibration.cancel(); }; }, []); return <React.Fragment />; }; export default HandleNotifications;
注意: Vibration.cancel() 可以停止振动。
处理权限
在 Android 和 iOS 上,你需要请求用户授权才能发送通知。 Expo 提供 Notifications.requestPermissionsAsync() 方法来处理权限请求。
import * as Notifications from 'expo-notifications'; import { Alert } from 'react-native'; async function configurePushNotifications() { const { status } = await Notifications.getPermissionsAsync(); let finalStatus = status; if (finalStatus !== 'granted') { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } if (finalStatus !== 'granted') { Alert.alert( "Permiso requerido", "Se requieren notificaciones locales para recibir alertas cuando vencen los recordatorios." ); return; } }
确保在组件挂载时调用 configurePushNotifications()。
在特定时间触发通知
要实现在特定时间触发通知,你需要使用 Notifications.scheduleNotificationAsync() 方法,并设置 trigger 属性。trigger 属性可以是一个 date 对象,也可以是一个包含 seconds 属性的对象,表示从现在开始的秒数。
import * as Notifications from 'expo-notifications'; async function schedulePushNotification() { await Notifications.scheduleNotificationAsync({ content: { title: "Reminder!", body: 'It's time!', sound: 'default', data: { data: 'goes here' }, }, trigger: { seconds: 5, repeats: false }, // Schedule for 5 seconds from now }); }
关键点:
- 使用 Notifications.scheduleNotificationAsync() 安排通知。
- trigger 属性控制通知触发时间。
总结
通过结合 expo-av 和 react-native 的 Vibration API,你可以轻松地在 Expo 应用中添加声音和振动通知。 确保正确处理权限,并使用 Notifications.scheduleNotificationAsync() 方法在特定时间触发通知。 记住,用户体验至关重要,合理使用通知可以提升应用的用户满意度。