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

React Native应用点击通知Snooze按钮崩溃问题求助

问题:React Native通知Snooze按钮点击导致应用崩溃

我开发了一个简单的React Native应用,包含“Set Reminder”按钮。点击按钮15秒后弹出带Snooze按钮的通知,同时设备持续振动10秒。预期点击Snooze按钮时,通知关闭、振动停止,5分钟后再次弹出相同通知并触发振动,但实际点击后应用崩溃,Logcat报错如下:

Invalid ID 0x00000000.
2024-07-16 09:20:13.583 15520-15615 RNPushNotification      com.stickynotes                      W  Unable to find com.dieam.reactnativepushnotification.notification_color in manifest. Falling back to default
2024-07-16 09:20:17.949 15520-15520 RNPushNotification      com.stickynotes                      I  RNPushNotificationBootEventReceiver loading scheduled notifications
2024-07-16 09:20:17.966 15520-15520 AndroidRuntime          com.stickynotes                      D  Shutting down VM
2024-07-16 09:20:17.972 15520-15520 AndroidRuntime          com.stickynotes                      E  FATAL EXCEPTION: main
                                                                                                    Process: com.stickynotes, PID: 15520
                                                                                                    java.lang.RuntimeException: Unable to start receiver com.dieam.reactnativepushnotification.modules.RNPushNotificationActions: java.lang.SecurityException: Permission Denial: android.intent.action.CLOSE_SYSTEM_DIALOGS broadcast from com.stickynotes (pid=15520, uid=10599) requires android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS.
                                                                                                        at android.app.ActivityThread.handleReceiver(ActivityThread.java:4506)
                                                                                                        at android.app.ActivityThread.-$$Nest$mhandleReceiver(Unknown Source:0)
                                                                                                        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2239)
                                                                                                        at android.os.Handler.dispatchMessage(Handler.java:106)
                                                                                                        at android.os.Looper.loopOnce(Looper.java:204)
                                                                                                        at android.os.Looper.loop(Looper.java:291)
                                                                                                        at android.app.ActivityThread.main(ActivityThread.java:8134)
                                                                                                        at java.lang.reflect.Method.invoke(Native Method)
                                                                                                        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:601)
                                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1019)
                                                                                                    Caused by: java.lang.SecurityException: Permission Denial: android.intent.action.CLOSE_SYSTEM_DIALOGS broadcast from com.stickynotes (pid=15520, uid=10599) requires android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS.
                                                                                                        at android.os.Parcel.createExceptionOrNull(Parcel.java:3021)
                                                                                                        at android.os.Parcel.createException(Parcel.java:3005)
                                                                                                        at android.os.Parcel.readException(Parcel.java:2988)
                                                                                                        at android.os.Parcel.readException(Parcel.java:2930)
                                                                                                        at android.app.IActivityManager$Stub$Proxy.broadcastIntentWithFeature(IActivityManager.java:5937)
                                                                                                        at android.app.ContextImpl.sendBroadcast(ContextImpl.java:1228)
                                                                                                        at android.content.ContextWrapper.sendBroadcast(ContextWrapper.java:500)
                                                                                                        at android.content.ContextWrapper.sendBroadcast(ContextWrapper.java:500)
                                                                                                        at com.dieam.reactnativepushnotification.modules.RNPushNotificationActions.onReceive(RNPushNotificationActions.java:65)
                                                                                                        at android.app.ActivityThread.handleReceiver(ActivityThread.java:4497)
                                                                                                        at android.app.ActivityThread.-$$Nest$mhandleReceiver(Unknown Source:0) 
                                                                                                        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2239) 
                                                                                                        at android.os.Handler.dispatchMessage(Handler.java:106) 
                                                                                                        at android.os.Looper.loopOnce(Looper.java:204) 
                                                                                                        at android.os.Looper.loop(Looper.java:291) 
                                                                                                        at android.app.ActivityThread.main(ActivityThread.java:8134) 
                                                                                                        at java.lang.reflect.Method.invoke(Native Method) 
                                                                                                        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:601) 
                                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1019) 
                                                                                                    Caused by: android.os.RemoteException: Remote stack trace:
                                                                                                        at com.android.server.wm.ActivityTaskManagerService.checkCanCloseSystemDialogs(ActivityTaskManagerService.java:3398)
                                                                                                        at com.android.server.wm.ActivityTaskManagerService$LocalService.checkCanCloseSystemDialogs(ActivityTaskManagerService.java:6079)
                                                                                                        at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:15304)
                                                                                                        at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:14819)
                                                                                                        at com.android.server.am.ActivityManagerService.broadcastIntentWithFeature(ActivityManagerService.java:15777)

我的App.js代码如下:

import React, { useEffect } from 'react';
import { View, Text, Button, StyleSheet, Vibration } from 'react-native';
import PushNotification from 'react-native-push-notification';

const App = () => {
  useEffect(() => {
    PushNotification.configure({
      onNotification: function (notification) {
        if (notification.action === 'Snooze') {
          handleSnoozeAction();
        }
      },
    });
  }, []);

  const handleSendReminder = () => {
    // Schedule notification after 15 seconds
    setTimeout(() => {
      PushNotification.localNotification({
        channelId: 'default-channel',
        title: 'Reminder Notification',
        message: "Don't forget to take a break!",
        actions: ['Snooze'],
      });

      // Start continuous vibration for 10 seconds
      const vibrationInterval = setInterval(() => {
        Vibration.vibrate(1000); // Vibrate for 1 second
      }, 1000);

      // Stop vibration after 10 seconds
      setTimeout(() => {
        clearInterval(vibrationInterval);
        Vibration.cancel();
      }, 10000); // 10 seconds
    }, 15000); // 15 seconds
  };

  const handleSnoozeAction = () => {
    // Clear all notifications and vibrations
    PushNotification.cancelAllLocalNotifications();
    Vibration.cancel();

    // Schedule a new notification and vibration after 2 minutes
    setTimeout(() => {
      PushNotification.localNotification({
        channelId: 'default-channel',
        title: 'Reminder Notification',
        message: "Don't forget to take a break!",
        actions: ['Snooze'],
      });

      // Start continuous vibration for 10 seconds
      const vibrationInterval = setInterval(() => {
        Vibration.vibrate(1000); // Vibrate for 1 second
      }, 1000);

      // Stop vibration after 10 seconds
      setTimeout(() => {
        clearInterval(vibrationInterval);
        Vibration.cancel();
      }, 10000); // 10 seconds
    }, 120000); // 2 minutes
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Welcome to My App</Text>
      <Button title="Set Reminder" onPress={handleSendReminder} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  title: {
    fontSize: 24,
    marginBottom: 20,
  },
});

export default App;

解决方案

核心问题分析

崩溃的直接原因是应用缺少BROADCAST_CLOSE_SYSTEM_DIALOGS权限,react-native-push-notification库在处理通知按钮点击时会发送系统广播,Android要求必须显式声明该权限才能执行此操作。另外日志中的警告是因为未配置通知颜色,也需要处理。

修复步骤

  1. 添加权限声明
    在android/app/src/main/AndroidManifest.xml中添加以下权限:

    <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
    
  2. 配置通知颜色
    在AndroidManifest.xml的<application>标签内添加元数据:

    <meta-data
        android:name="com.dieam.reactnativepushnotification.notification_color"
        android:resource="@color/white" />
    

    若android/app/src/main/res/values/colors.xml不存在则创建,添加颜色值:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <color name="white">#FFFFFF</color>
    </resources>
    
  3. 优化定时器清理逻辑
    原代码中定时器未在组件卸载时清理,可能导致内存泄漏,修改useEffect添加清理函数:

    useEffect(() => {
      let timers = {
        reminder: null,
        snooze: null,
        vibrationStop: null,
        vibrationInterval: null
      };
    
      PushNotification.configure({
        onNotification: function (notification) {
          if (notification.action === 'Snooze') {
            handleSnoozeAction(timers);
          }
        },
      });
    
      return () => {
        // 清理所有定时器
        Object.values(timers).forEach(timer => {
          if (timer) {
            if (typeof timer === 'number') clearTimeout(timer);
            else clearInterval(timer);
          }
        });
        Vibration.cancel();
        PushNotification.cancelAllLocalNotifications();
      };
    }, []);
    

    同时修改handleSendReminder和handleSnoozeAction,将定时器存入timers对象以便统一清理。

  4. 使用原生调度替代setTimeout
    原生localNotificationSchedule比setTimeout更可靠,尤其是应用在后台时,替换后的代码:

    const handleSendReminder = (timers) => {
      // 15秒后触发通知
      PushNotification.localNotificationSchedule({
        channelId: 'default-channel',
        title: 'Reminder Notification',
        message: "Don't forget to take a break!",
        actions: ['Snooze'],
        date: new Date(Date.now() + 15000),
      });
    
      // 振动逻辑
      timers.vibrationInterval = setInterval(() => {
        Vibration.vibrate(1000);
      }, 1000);
    
      timers.vibrationStop = setTimeout(() => {
        clearInterval(timers.vibrationInterval);
        Vibration.cancel();
      }, 10000);
    };
    
    const handleSnoozeAction = (timers) => {
      PushNotification.cancelAllLocalNotifications();
      Vibration.cancel();
    
      // 2分钟后重新调度通知
      PushNotification.localNotificationSchedule({
        channelId: 'default-channel',
        title: 'Reminder Notification',
        message: "Don't forget to take a break!",
        actions: ['Snooze'],
        date: new Date(Date.now() + 120000),
      });
    
      // 振动逻辑
      timers.vibrationInterval = setInterval(() => {
        Vibration.vibrate(1000);
      }, 1000);
    
      timers.vibrationStop = setTimeout(() => {
        clearInterval(timers.vibrationInterval);
        Vibration.cancel();
      }, 10000);
    };
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 18:07:03