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

如何使用AlarmManager实现每日/每周重复提醒?浇水闹钟不重复问题解决

浇水提醒仅触发一次无法重复的问题修复

核心问题分析

  1. PendingIntent标记错误:低版本系统中使用了FLAG_ONE_SHOT,这个标记会让PendingIntent触发一次后立即失效,导致后续重复闹钟无法正常触发。
  2. 时间逻辑冲突:scheduleAlarm方法中同时混用了Calendar设置星期几和datePlanted + timeTonotify解析时间的逻辑,可能导致目标时间计算错误。
  3. 高版本AlarmManager限制:Android 6.0+对后台闹钟有严格限制,setRepeating在高版本系统中无法保证精准重复,需结合setExactAndAllowWhileIdle手动重新调度。

修复方案

1. 修正PendingIntent标记

将低版本中的FLAG_ONE_SHOT替换为FLAG_UPDATE_CURRENT,保证PendingIntent可以重复使用;高版本同时保留FLAG_IMMUTABLE以符合系统要求。

2. 统一时间计算逻辑

放弃混用两种时间设置方式,基于Calendar直接计算目标触发时间,并处理当前时间已过的情况。

3. 适配高版本重复闹钟

在广播接收器中收到闹钟触发后,手动重新调度下一次闹钟,避免依赖setRepeating的不可靠行为。

修改后的关键代码

调度闹钟方法scheduleAlarm

private void scheduleAlarm(String text, int dayOfWeek, long time, int intervalTime) {
    AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    Calendar calendar = Calendar.getInstance();
    
    // 设置目标时间的时分秒(假设time为时间选择器返回的毫秒数,对应时分)
    calendar.set(Calendar.HOUR_OF_DAY, (int) (time / 3600000));
    calendar.set(Calendar.MINUTE, (int) ((time % 3600000) / 60000));
    calendar.set(Calendar.SECOND, 0);
    
    // 每周重复时设置对应星期几
    if (intervalTime == 7 * 24 * 60 * 60 * 1000) {
        calendar.set(Calendar.DAY_OF_WEEK, dayOfWeek);
    }
    
    // 若当前时间已过目标时间,往后推一个周期
    if (calendar.getTimeInMillis() < System.currentTimeMillis()) {
        calendar.add(Calendar.MILLISECOND, intervalTime);
    }
    
    Intent intent = new Intent(getApplicationContext(), AlarmBroadCastForTrack.class);
    intent.putExtra("message", text);
    intent.putExtra("interval", intervalTime); // 传递周期,用于后续重新调度
    
    // 构建可重复使用的PendingIntent
    PendingIntent pendingIntent;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, 
            PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
    } else {
        pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, 
            PendingIntent.FLAG_UPDATE_CURRENT);
    }
    
    // 适配不同版本的AlarmManager API
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        am.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    } else {
        am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), intervalTime, pendingIntent);
    }
    
    Toast.makeText(this, "Alarm Setup Successful", Toast.LENGTH_SHORT).show();
}

广播接收器AlarmBroadCastForTrack

public class AlarmBroadCastForTrack extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle bundle = intent.getExtras();
        String text = bundle.getString("message");
        int interval = bundle.getInt("interval");
        
        // 构建并显示通知
        Intent intent1 = new Intent(context, NotificationMessageForTrack.class);
        intent1.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intent1.putExtra("message", text);
        
        PendingIntent pendingIntent;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            pendingIntent = PendingIntent.getActivity(context, 1, intent1, PendingIntent.FLAG_IMMUTABLE);
        } else {
            pendingIntent = PendingIntent.getActivity(context, 1, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
        }
        
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context, "notify_001");
        
        RemoteViews contentView = new RemoteViews(context.getPackageName(), R.layout.notification_layout_for_track);
        contentView.setImageViewResource(androidx.appcompat.R.id.image, R.mipmap.ic_launcher);
        contentView.setTextViewText(R.id.message, text);
        
        mBuilder.setSmallIcon(R.drawable.ic_baseline_alarm_on_24);
        mBuilder.setAutoCancel(true);
        mBuilder.setPriority(Notification.PRIORITY_HIGH);
        mBuilder.setContent(contentView);
        mBuilder.setContentIntent(pendingIntent);
        
        // 适配Android 8.0+通知渠道
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            String channelId = "channel_id";
            if (notificationManager.getNotificationChannel(channelId) == null) {
                NotificationChannel channel = new NotificationChannel(channelId, "浇水提醒", NotificationManager.IMPORTANCE_HIGH);
                channel.enableVibration(true);
                notificationManager.createNotificationChannel(channel);
            }
            mBuilder.setChannelId(channelId);
        }
        
        Notification notification = mBuilder.build();
        notificationManager.notify(1, notification);
        
        // 重新调度下一次闹钟
        rescheduleAlarm(context, text, interval);
    }
    
    private void rescheduleAlarm(Context context, String text, int interval) {
        AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MILLISECOND, interval);
        
        Intent intent = new Intent(context, AlarmBroadCastForTrack.class);
        intent.putExtra("message", text);
        intent.putExtra("interval", interval);
        
        PendingIntent pendingIntent;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 
                PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
        } else {
            pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 
                PendingIntent.FLAG_UPDATE_CURRENT);
        }
        
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            am.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
        } else {
            am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), interval, pendingIntent);
        }
    }
}

额外注意事项

  • 确保应用已申请RECEIVE_BOOT_COMPLETED权限,并在广播接收器中监听开机广播,重启后重新调度闹钟,避免设备重启后闹钟失效。
  • 对于Android 12+,需在Manifest中声明USE_EXACT_ALARM权限,确保精准闹钟可以正常触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 14:25:16