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

Flutter Alarm包周重复闹钟失效及后台不响问题排查

Flutter Alarm Package 周重复闹钟问题解决方案(Android)

一、确保应用被杀后每周选中日期闹钟正常触发

  • 申请精确闹钟权限
    Android 12及以上系统要求SCHEDULE_EXACT_ALARM权限才能触发无延迟的精确闹钟,先在AndroidManifest.xml中添加权限声明:

    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
    

    再在Flutter代码中动态申请权限:

    import 'package:permission_handler/permission_handler.dart';
    
    Future<void> requestExactAlarmPermission() async {
      final status = await Permission.scheduleExactAlarm.request();
      if (!status.isGranted) {
        openAppSettings();
      }
    }
    
  • 注册开机与应用更新广播接收器
    系统重启或应用更新后,所有已设置的闹钟会被清除,需在开机后重新加载重复规则并设置闹钟。在AndroidManifest.xml中注册广播接收器:

    <receiver android:name=".AlarmBootReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
        </intent-filter>
    </receiver>
    

    创建AlarmBootReceiver类,在onReceive方法中读取SharedPreferences保存的重复日期,重新计算并设置闹钟:

    class AlarmBootReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            if (intent?.action == Intent.ACTION_BOOT_COMPLETED || 
                intent?.action == Intent.ACTION_MY_PACKAGE_REPLACED) {
                val prefs = context?.getSharedPreferences("alarm_prefs", Context.MODE_PRIVATE)
                val selectedDays = prefs?.getStringSet("selected_days", emptySet()) ?: emptySet()
                // 通过MethodChannel调用Flutter端的闹钟设置逻辑
            }
        }
    }
    
  • 启用前台服务模式
    将响铃服务设为前台服务,避免系统在后台杀掉进程。设置闹钟时开启相关配置:

    await Alarm.set(
      alarmSettings: AlarmSettings(
        id: alarmId,
        dateTime: nextTriggerTime,
        notificationTitle: "闹钟提醒",
        notificationBody: "该起床了!",
        enableNotificationOnKill: true,
        foregroundMode: true,
      ),
    );
    
  • 引导用户关闭电池优化
    国产ROM的电池优化会强制清理后台进程,需引导用户将应用加入白名单:

    Future<void> requestIgnoreBatteryOptimizations() async {
      final status = await Permission.ignoreBatteryOptimizations.request();
      if (!status.isGranted) {
        openAppSettings();
      }
    }
    

二、避免停止闹钟后删除重复规则,确保后续日期正常触发

核心逻辑:不依赖alarm package的重复机制,手动维护重复规则,每次响铃结束后自动设置下一次闹钟

  • 分离重复规则与闹钟实例
    将用户选中的重复日期(如["FRIDAY", "SATURDAY"])持久化存储在SharedPreferences中,不要与单个闹钟实例绑定。

  • 修改响铃页面的停止逻辑
    用户点击停止响铃时,先计算下一个触发日期并设置新闹钟,再停止当前响铃:

    // 计算下一次触发日期
    DateTime calculateNextTriggerTime(Set<String> selectedDays, DateTime currentAlarmTime) {
      DateTime now = DateTime.now();
      DateTime nextTime = DateTime(now.year, now.month, now.day);
      bool found = false;
    
      // 遍历一周找最近的符合条件的日期
      for (int i = 0; i < 7; i++) {
        DateTime candidate = nextTime.add(Duration(days: i));
        String dayName = DateFormat('EEEE').format(candidate).toUpperCase();
        if (selectedDays.contains(dayName) && candidate.isAfter(now)) {
          nextTime = DateTime(
            candidate.year, candidate.month, candidate.day,
            currentAlarmTime.hour, currentAlarmTime.minute
          );
          found = true;
          break;
        }
      }
    
      // 本周无符合日期则取下周第一个目标日期
      if (!found) {
        for (String day in selectedDays) {
          int targetWeekday = _getWeekdayNumber(day);
          int daysToAdd = (targetWeekday - now.weekday + 7) % 7;
          if (daysToAdd == 0) daysToAdd = 7;
          nextTime = now.add(Duration(days: daysToAdd));
          nextTime = DateTime(
            nextTime.year, nextTime.month, nextTime.day,
            currentAlarmTime.hour, currentAlarmTime.minute
          );
          break;
        }
      }
      return nextTime;
    }
    
    // 星期名称转数字(1=周一,7=周日)
    int _getWeekdayNumber(String dayName) {
      switch (dayName) {
        case "MONDAY": return 1;
        case "TUESDAY": return 2;
        case "WEDNESDAY": return 3;
        case "THURSDAY": return 4;
        case "FRIDAY": return 5;
        case "SATURDAY": return 6;
        case "SUNDAY": return 7;
        default: return 1;
      }
    }
    
    // 停止闹钟并设置下一次
    Future<void> stopAlarmAndSetNext(int currentAlarmId, DateTime currentAlarmTime) async {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      Set<String> selectedDays = prefs.getStringSet("selected_days") ?? {};
    
      if (selectedDays.isNotEmpty) {
        DateTime nextTime = calculateNextTriggerTime(selectedDays, currentAlarmTime);
        await Alarm.set(
          alarmSettings: AlarmSettings(
            id: currentAlarmId,
            dateTime: nextTime,
            notificationTitle: "闹钟提醒",
            notificationBody: "该起床了!",
            enableNotificationOnKill: true,
            foregroundMode: true,
          ),
        );
      }
    
      await Alarm.stop(currentAlarmId);
    }
    
  • 禁止主动删除闹钟实例
    仅在用户主动取消整个重复规则时调用Alarm.delete(),停止响铃仅使用Alarm.stop(),避免删除重复逻辑的载体。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 11:15:12