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

Android Studio使用AlarmManager设置多组闹钟触发异常问题

问题根因

  • 使用了setInexactRepeating闹钟接口:Android 4.4及以上版本该接口默认会对闹钟触发时间做批量对齐来降低功耗,天生存在数分钟级的延迟,无法满足准时触发要求
  • 通知ID固定:所有通知调用managerCompat.notify(15, notificationCompat)时均使用相同ID,后触发的通知会直接覆盖未展示的前序通知,导致多个闹钟仅能看到最后一条
  • PendingIntent Flag不兼容高版本系统:PendingIntent.getBroadcast使用flag=0,未指定可变/不可变属性,Android 12及以上系统会直接拦截此类PendingIntent,导致闹钟无法触发
  • 代码语法错误:广播接收器中createNotificationChannel方法写在了onReceive方法内部,Java不支持方法嵌套,会直接编译失败
  • 未适配高版本权限:Android 12及以上要求应用持有SCHEDULE_EXACT_ALARM权限才能使用精准闹钟,Android 13还需要POST_NOTIFICATIONS权限才能弹出通知
  • 时间戳未做边界处理:如果传入的UhrzeitInMillis是当天已过的时间,不会自动跳转到第二天,导致闹钟不会触发

修正代码方案

1. 清单文件添加权限与广播注册

<!-- 唤醒锁权限,保证休眠时闹钟能唤醒设备 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Android 12+ 精准闹钟权限 -->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<!-- Android 13+ 通知权限 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- 监听设备开机广播,重启后可重新设置闹钟 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<!-- 静态注册广播接收器,避免APP被后台清理后无法接收闹钟广播 -->
<receiver android:name=".Optionen_Alarm">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

2. 修改闹钟设置方法

建议给4个时间选择器分别分配固定的requestCode(0/1/2/3),不要用动态生成的时间戳,避免后续无法找到对应闹钟进行更新/取消操作。

/**
 * 设置精准闹钟
 * @param requestCode 每个闹钟对应唯一固定值:0/1/2/3
 */
public void SetAlarm(Context context, long uhrzeitInMillis, int requestCode) {
    // 边界处理:如果所选时间早于当前时间,自动加一天推迟到次日触发
    if (uhrzeitInMillis < System.currentTimeMillis()) {
        uhrzeitInMillis += AlarmManager.INTERVAL_DAY;
    }

    Intent intent = new Intent(context, Optionen_Alarm.class);
    // 携带闹钟ID,后续用来区分不同闹钟的通知
    intent.putExtra("alarm_id", requestCode);
    // 适配高版本PendingIntent flag要求
    int flag = PendingIntent.FLAG_UPDATE_CURRENT;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        flag |= PendingIntent.FLAG_IMMUTABLE;
    }
    PendingIntent pendingIntent = PendingIntent.getBroadcast(
            context.getApplicationContext(), requestCode, intent, flag);
    AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

    // 用精准闹钟接口,保证休眠状态下也能准时触发
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, uhrzeitInMillis, pendingIntent);
    } else {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, uhrzeitInMillis, pendingIntent);
    }
}

3. 修改广播接收器代码

public class Optionen_Alarm extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "myapp:mywakelocktag");
        // 加10秒超时,避免异常情况下唤醒锁不释放导致耗电
        wl.acquire(10 * 1000L);

        // 获取当前闹钟ID,作为通知ID避免互相覆盖
        int alarmId = intent.getIntExtra("alarm_id", 0);
        createNotificationChannel(context);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "1");
        builder.setContentTitle("定时提醒");
        builder.setContentText("你设置的时间到了!");
        builder.setSmallIcon(R.drawable.picture);
        builder.setColor(context.getResources().getColor(R.color.red));
        builder.setVibrate(new long[]{0, 300, 300, 300});
        builder.setLights(Color.WHITE, 1000, 5000);
        builder.setAutoCancel(true);
        builder.setPriority(NotificationCompat.PRIORITY_HIGH);
        builder.setStyle(new NotificationCompat.BigTextStyle().bigText("你设置的时间到了!"));

        Intent notifyIntent = new Intent(context, Activity.class);
        int flag = PendingIntent.FLAG_UPDATE_CURRENT;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            flag |= PendingIntent.FLAG_IMMUTABLE;
        }
        PendingIntent pendingIntent = PendingIntent.getActivity(context, alarmId, notifyIntent, flag);
        builder.setContentIntent(pendingIntent);

        Notification notificationCompat = builder.build();
        NotificationManagerCompat managerCompat = NotificationManagerCompat.from(context);
        // 用不同的alarmId作为通知ID,不会互相覆盖
        managerCompat.notify(alarmId, notificationCompat);

        // 重新设置下一天的闹钟,实现每日重复
        // 注意:需要提前将每个alarmId对应的时间毫秒值存储到SharedPreferences等持久化容器中
        // long savedTime = 从持久化容器取出对应alarmId的时间戳;
        // SetAlarm(context, savedTime, alarmId);

        wl.release();
    }

    // 方法移到onReceive外部,修复语法错误
    private void createNotificationChannel(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            CharSequence name = "定时提醒";
            String description = "每日定时提醒通知";
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel channel = new NotificationChannel("1", name, importance);
            channel.setDescription(description);
            channel.enableVibration(true);
            channel.setVibrationPattern(new long[]{0, 300, 300, 300});
            NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
            notificationManager.createNotificationChannel(channel);
        }
    }
}

额外注意事项

  • Android 12及以上需要在代码中动态申请SCHEDULE_EXACT_ALARM权限,Android 13及以上需要动态申请POST_NOTIFICATIONS权限,无权限时闹钟和通知都无法正常工作
  • 国产ROM(小米、华为、OPPO等)需要手动给APP开启自启动权限、后台弹出界面权限、允许通知权限,否则系统会拦截后台闹钟和通知
  • 如果需要设备重启后闹钟依然生效,需要在收到BOOT_COMPLETED广播时,重新设置所有已保存的闹钟

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 10:36:03