You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Expo笔记应用中实现带音效、震动的持久到期通知?

问题解决:笔记到期通知的音效、震动及持久化横幅实现

核心错误分析

  • 未监听通知到达事件:仅监听了用户点击通知的addNotificationResponseReceivedListener,但通知主动弹出时的触发逻辑缺失,导致音效/震动无法在通知到达时启动
  • 音效触发时机错误:修改后的代码在组件挂载时直接播放音效,而非绑定到通知到达事件
  • 震动逻辑未关联通知:没有在通知到达时调用震动API
  • 持久化横幅未配置:创建本地通知时未设置对应系统的持久化属性(iOS用sticky,Android用ongoing)
  • 应用焦点处理错误:误用通知响应监听来控制音频,应使用AppState监听应用前后台状态

修正后的完整代码

import { useEffect, useState } from "react";
import * as Notifications from "expo-notifications";
import { Alert, Vibration, AppState } from "react-native";
import { useNavigation } from "@react-navigation/native";
import { Audio } from 'expo-av';

// 配置通知处理器
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldPlaySound: false, // 关闭系统默认音效,使用自定义音效
    shouldSetBadge: false,
    shouldShowAlert: true,
  }),
});

const HandleNotifications = () => {
  const navigation = useNavigation();
  const [sound, setSound] = useState(null);
  const [appState, setAppState] = useState(AppState.currentState);

  // 加载自定义音效
  async function loadCustomSound() {
    try {
      const { sound: soundObject } = await Audio.Sound.createAsync(
        require('../assets/sonido.mp3')
      );
      setSound(soundObject);
    } catch (error) {
      console.error("加载音效失败:", error);
    }
  }

  // 请求通知权限并加载音效
  useEffect(() => {
    async function configureNotifications() {
      const { status } = await Notifications.getPermissionsAsync();
      let finalStatus = status;

      if (finalStatus !== "granted") {
        const { status: newStatus } = await Notifications.requestPermissionsAsync();
        finalStatus = newStatus;
      }

      if (finalStatus !== "granted") {
        Alert.alert(
          "权限要求",
          "需要本地通知权限才能接收笔记到期提醒"
        );
        return;
      }

      await loadCustomSound();
    }

    configureNotifications();
  }, []);

  // 监听通知到达事件:触发音效和震动
  useEffect(() => {
    const notificationReceivedSubscription = Notifications.addNotificationReceivedListener(async () => {
      // 触发震动(持续1秒,可自定义时长或震动模式)
      Vibration.vibrate(1000);

      // 播放自定义音效
      if (sound) {
        await sound.replayAsync(); // 重播音效,避免重复加载资源
      }
    });

    // 监听通知点击事件:导航到对应页面并停止震动
    const notificationResponseSubscription = Notifications.addNotificationResponseReceivedListener((response) => {
      Vibration.cancel();
      const reminderId = response.notification.request.content.data.reminderId;
      if (reminderId) {
        navigation.navigate("ModifyReminder", { reminderId });
      }
    });

    return () => {
      notificationReceivedSubscription.remove();
      notificationResponseSubscription.remove();
    };
  }, [sound, navigation]);

  // 监听应用前后台状态,处理音效资源
  useEffect(() => {
    const appStateSubscription = AppState.addEventListener('change', nextAppState => {
      if (appState.match(/inactive|background/) && nextAppState === 'active') {
        // 应用回到前台,停止播放中的音效
        if (sound) {
          sound.stopAsync();
        }
      }
      setAppState(nextAppState);
    });

    return () => {
      appStateSubscription.remove();
      if (sound) {
        sound.unloadAsync();
      }
    };
  }, [sound, appState]);

  // ------------------------------
  // 注意:创建本地通知时需添加持久化横幅配置
  // 示例创建通知的代码(需在你的笔记到期逻辑中调用):
  // async function scheduleReminderNotification(reminderId, title, body) {
  //   // 提前创建Android通知渠道(仅需执行一次)
  //   await Notifications.setNotificationChannelAsync('reminder-channel', {
  //     name: '笔记到期提醒',
  //     importance: Notifications.AndroidImportance.MAX,
  //   });

  //   await Notifications.scheduleNotificationAsync({
  //     content: {
  //       title,
  //       body,
  //       data: { reminderId },
  //     },
  //     trigger: { seconds: 10 }, // 测试用10秒后触发,替换为你的到期时间
  //     // 配置持久化横幅
  //     ios: {
  //       sticky: true, // iOS:通知横幅保持到手动关闭
  //     },
  //     android: {
  //       channelId: "reminder-channel",
  //       ongoing: true, // Android:通知为持续通知,需手动清除
  //       priority: Notifications.AndroidNotificationPriority.MAX,
  //     },
  //   });
  // }
  // ------------------------------

  return null;
};

export default HandleNotifications;

关键修改说明

  1. 新增通知到达监听:通过addNotificationReceivedListener在通知弹出时立即触发震动和自定义音效
  2. 修正音效触发逻辑:在通知到达时调用sound.replayAsync(),避免组件挂载就自动播放
  3. 添加震动控制:通知到达时触发震动,用户点击通知时停止震动
  4. 持久化横幅配置:创建通知时,iOS设置sticky: true,Android设置ongoing: true并提前配置通知渠道
  5. 应用状态监听修复:使用AppState监听应用前后台切换,合理管理音效资源
  6. 关闭系统默认音效:将shouldPlaySound设为false,避免和自定义音效冲突

内容的提问来源于stack exchange,提问作者Miguel Espeso

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 17:42:03