如何使用AlarmManager设置周期性提醒?求多目标提醒实现教程
Hey there! Let's walk through how to build this reminder system for your app—this is a super common scenario, so I’ll break it down into actionable steps with code examples and key tips.
Here’s the big-picture workflow you’ll need to follow:
- Fetch all reminder configurations from your SQLite database when the app starts (or when configurations are updated).
- For each reminder, calculate the exact trigger time based on its type (one-time date or weekly repeat).
- Use
AlarmManagerto schedule the reminder, ensuring it fires even in Doze mode (critical for modern Android versions). - When the alarm triggers, broadcast an intent to show a system notification. For weekly repeats, re-schedule the next alarm after the current one fires.
First, let’s assume your reminders table has columns like:id (unique ID), target_name (your goal’s name), trigger_type (0 for one-time date, 1 for weekly), trigger_date (ISO string for one-time triggers), trigger_weekdays (bitmask or comma-separated values for days e.g., "1,3" for Monday/Wednesday), trigger_time (HH:mm string).
Here’s how you might load these into a model class:
// Example Reminder POJO public class Reminder { private int id; private String targetName; private int triggerType; private Date triggerDate; private List<Integer> triggerWeekdays; // 1=Monday, 7=Sunday private LocalTime triggerTime; // Getters and setters } // Fetch from SQLite public List<Reminder> loadActiveReminders(SQLiteDatabase db) { List<Reminder> reminders = new ArrayList<>(); Cursor cursor = db.rawQuery("SELECT * FROM reminders WHERE is_active = 1", null); while (cursor.moveToNext()) { Reminder rem = new Reminder(); rem.setId(cursor.getInt(cursor.getColumnIndexOrThrow("id"))); rem.setTargetName(cursor.getString(cursor.getColumnIndexOrThrow("target_name"))); rem.setTriggerType(cursor.getInt(cursor.getColumnIndexOrThrow("trigger_type"))); // Parse date, weekdays, and time based on your storage format // ... reminders.add(rem); } cursor.close(); return reminders; }
AlarmManager is the Android system service for scheduling future events. We’ll handle both one-time and weekly repeat cases.
3.1 One-Time Date Reminders
For a specific date and time, calculate the target timestamp and use setExactAndAllowWhileIdle (to bypass Doze mode):
private void scheduleOneTimeReminder(Context context, Reminder reminder) { Calendar targetCal = Calendar.getInstance(); targetCal.setTime(reminder.getTriggerDate()); targetCal.set(Calendar.HOUR_OF_DAY, reminder.getTriggerTime().getHour()); targetCal.set(Calendar.MINUTE, reminder.getTriggerTime().getMinute()); targetCal.set(Calendar.SECOND, 0); long triggerMillis = targetCal.getTimeInMillis(); // If the target time is in the past, skip or adjust (up to your logic) if (triggerMillis < System.currentTimeMillis()) return; AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, ReminderReceiver.class); intent.putExtra("REMINDER_ID", reminder.getId()); // Ensure unique PendingIntent using the reminder ID as request code PendingIntent pendingIntent = PendingIntent.getBroadcast( context, reminder.getId(), intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); // Schedule exact alarm that works in Doze mode if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerMillis, pendingIntent); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerMillis, pendingIntent); } else { alarmManager.set(AlarmManager.RTC_WAKEUP, triggerMillis, pendingIntent); } }
3.2 Weekly Repeating Reminders
For weekly repeats, you’ll need to handle each selected weekday individually. Note that setRepeating is imprecise on API 19+, so we’ll use exact alarms and re-schedule after each trigger:
private void scheduleWeeklyReminder(Context context, Reminder reminder) { for (int weekday : reminder.getTriggerWeekdays()) { Calendar targetCal = Calendar.getInstance(); // Map your weekday value to Calendar constants (1=Monday → Calendar.MONDAY) targetCal.set(Calendar.DAY_OF_WEEK, weekday); targetCal.set(Calendar.HOUR_OF_DAY, reminder.getTriggerTime().getHour()); targetCal.set(Calendar.MINUTE, reminder.getTriggerTime().getMinute()); targetCal.set(Calendar.SECOND, 0); // If today's weekday has passed, set to next week if (targetCal.getTimeInMillis() < System.currentTimeMillis()) { targetCal.add(Calendar.WEEK_OF_YEAR, 1); } long triggerMillis = targetCal.getTimeInMillis(); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, ReminderReceiver.class); intent.putExtra("REMINDER_ID", reminder.getId()); intent.putExtra("WEEKDAY", weekday); // Pass weekday to re-schedule later PendingIntent pendingIntent = PendingIntent.getBroadcast( context, reminder.getId() * 10 + weekday, // Unique request code per weekday intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerMillis, pendingIntent); } else { alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerMillis, pendingIntent); } } }
Create a BroadcastReceiver to handle alarm triggers and show notifications. For weekly repeats, re-schedule the next alarm here:
public class ReminderReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { int reminderId = intent.getIntExtra("REMINDER_ID", -1); if (reminderId == -1) return; // Fetch reminder details from SQLite Reminder reminder = getReminderById(context, reminderId); if (reminder == null) return; // Show notification NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "REMINDER_CHANNEL") .setSmallIcon(R.drawable.ic_reminder) .setContentTitle("Goal Reminder") .setContentText("Time to work on: " + reminder.getTargetName()) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setAutoCancel(true); notificationManager.notify(reminderId, builder.build()); // Re-schedule weekly reminder if (reminder.getTriggerType() == 1) { int weekday = intent.getIntExtra("WEEKDAY", -1); if (weekday != -1) { Calendar nextCal = Calendar.getInstance(); nextCal.set(Calendar.DAY_OF_WEEK, weekday); nextCal.set(Calendar.HOUR_OF_DAY, reminder.getTriggerTime().getHour()); nextCal.set(Calendar.MINUTE, reminder.getTriggerTime().getMinute()); nextCal.add(Calendar.WEEK_OF_YEAR, 1); // Re-schedule the exact alarm for next week AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent nextIntent = new Intent(context, ReminderReceiver.class); nextIntent.putExtra("REMINDER_ID", reminderId); nextIntent.putExtra("WEEKDAY", weekday); PendingIntent pendingIntent = PendingIntent.getBroadcast( context, reminderId * 10 + weekday, nextIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextCal.getTimeInMillis(), pendingIntent); } else { alarmManager.setExact(AlarmManager.RTC_WAKEUP, nextCal.getTimeInMillis(), pendingIntent); } } } } // Helper method to fetch reminder from DB private Reminder getReminderById(Context context, int id) { // Implement your SQLite query here return null; } }
Don’t forget to register the receiver in your AndroidManifest.xml:
<receiver android:name=".ReminderReceiver" />
- Notification Channels: Android 8.0+ requires a notification channel. Create one on app startup:
private void createReminderChannel(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( "REMINDER_CHANNEL", "Goal Reminders", NotificationManager.IMPORTANCE_DEFAULT ); channel.setDescription("Notifications for your goal reminders"); NotificationManager manager = context.getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } } - Cancel Old Alarms: When a reminder is edited or deleted, cancel its associated
PendingIntentusingalarmManager.cancel(pendingIntent). - Handle Reboots: If you want reminders to persist after a device reboot, register a
BootCompletedReceiverto re-schedule all active reminders when the device starts.
内容的提问来源于stack exchange,提问作者Abaneel Jahid

