This example demonstrate daily alarm at specific time (e.g. 8.30am daily).
- Read about Caveats of Repeating Alarm.
- Use
setExact
/setWindow
/set
to start alarm at exact timing (orsetExactAndAllowWhileIdle
for extreme accuracy), then set alarm again atonReceive
for the next day. Don't usesetInexactRepeating
orsetRepeating
due to time drift and inability to guarantee execution at exact time.
class ReminderReceiver : BroadcastReceiver() { companion object { private const val REQUEST_TIMER1 = 1 private const val PARAM_NAME = "name" fun startAlarm(context: Context, startMillis: Long? = null) { val pendingIntent = getIntent(context, REQUEST_TIMER1, "Alarm at 8.30am") val alarm = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager // trigger at 8:30am val alarmTime = LocalTime.of(8, 30) var now = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES) val nowTime = now.toLocalTime() // if same time, schedule for next day as well // if today's time had passed, schedule for next day if (nowTime == alarmTime || nowTime.isAfter(alarmTime)) { now = now.plusDays(1) } now = now.withHour(alarmTime.hour).withMinute(alarmTime.minute) // .withSecond(alarmTime.second).withNano(alarmTime.nano) // alarm use UTC/GMT time val utc = now.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime() val startMillis = utc.atZone(ZoneOffset.UTC)!!.toInstant()!!.toEpochMilli() Timber.d("Alarm will trigger in ${(startMillis-System.currentTimeMillis())/1000}s") // AlarmManagerCompat.setExact(alarm, AlarmManager.RTC_WAKEUP, startMillis, pendingIntent!!) if (Build.VERSION.SDK_INT >= 19) { // alarm.setExact(AlarmManager.RTC_WAKEUP, startMillis, pendingIntent) // effort to save battery, allow deviation of 15 minutes val windowMillis = 15L * 60L * 1_000L alarm.setWindow(AlarmManager.RTC_WAKEUP, startMillis, windowMillis, pendingIntent) } else { alarm.set(AlarmManager.RTC_WAKEUP, startMillis, pendingIntent) } } fun cancelAlarm(context: Context) { val pendingIntent = getIntent(context, REQUEST_TIMER1) val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmManager.cancel(pendingIntent) } } override fun onReceive(context: Context, intent: Intent) { try { val name = intent.getStringExtra(PARAM_NAME) Timber.d("onReceive, name=$name") Toast.makeText(context, name, Toast.LENGTH_LONG).show() } catch (e: Exception) { Timber.e(e) } finally { // use try/catch to make sure next day alarm is set even if exception happens // start alarm for next day startAlarm(context) } }}
Edit AndroidManifest.xml
.
```xml
<manifest ..>
<application ...>
<receiver android:name=".ReminderReceiver" />
</application>
</manifest>
Usage
ReminderReceiver.startAlarm(context)
NOTE: The AlarmManager
won't survive a device reboot. You have to implement a device bootup receiver and call ReminderReceiver.startAlarm(context)
.
NOTE: Refer Setup Android Notification.
NOTE: To support multiple alarm, use a different requestCode
for each alarm. Refer Android AlarmManager: Multiple Alarm With Arguments/Parameters.
NOTE: AlarmManager is removed when the app is uninstalled, and it seems the Alarm is cancelled after APK update (not sure if this is true or always true).
You might want to explore Android Schedule Daily Repeating/Reminder Alarm at Specific Time With WorkManager.
References: