Note: I tried various solutions that are written about here on StackOverflow (example here). Please do not close this without checking if your solution from what you've found works using the test I've written below.
Background
There is a requirement on the app, that the user sets a reminder to be scheduled at a specific time, so when the app gets triggered on this time, it does something tiny in the background (just some DB query operation), and shows a simple notification, to tell about the reminder.
In the past, I used a simple code to set something to be scheduled at a relatively specific time:
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
when {
VERSION.SDK_INT >= VERSION_CODES.KITKAT -> alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
else -> alarmManager.set(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
}
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("AppLog", "AlarmReceiver onReceive")
//do something in the real app
}
}
Usage:
val timeToTrigger = System.currentTimeMillis() + java.util.concurrent.TimeUnit.MINUTES.toMillis(1)
setAlarm(this, timeToTrigger, 1)
The problem
I've now tested this code on emulators on new Android versions and on Pixel 4 with Android 10, and it doesn't seem to trigger, or maybe it triggers after a very long time since what I provide it. I'm well aware of the terrible behavior that some OEMs added to removing apps from the recent tasks, but this one is on both emulators and Pixel 4 device (stock).
I've read on the docs about setting an alarm, that it got restricted for apps so that it won't occur too often, but this doesn't explain how to set an alarm at a specific time, and it doesn't explain how come Google's Clock app succeeds doing it.
Not only that, but according to what I understand, it says the restrictions should be applied especially for low power state of the device, but in my case, I didn't have this state, on both the device and on the emulators. I've set the alarms to be triggered in about a minute from now.
Seeing that many alarm clock apps don't work anymore as they used to, I think there is something that is missing on the docs. Example of such apps is the popular Timely app that was bought by Google but never got new updates to handle the new restrictions, and now users want it back.. However, some popular apps do work fine, such as this one.
What I've tried
To test that indeed the alarm works, I perform these tests when trying to trigger the alarm in a minute from now, after installing the app for the first time, all while the device is connected to the PC (to see the logs) :
- Test when the app is in the foreground, visible to the user. - took 1-2 minutes.
- Test when the app was sent to the background (using the home button, for example) - took about 1 minute
- Test when app's task was removed from the recent tasks. - I waited more than 20 minutes and didn't see the alarm being triggered, writing to logs.
- Like #3, but also turn off the screen. It would probably be worse...
I tried to use the next things, all don't work:
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
combination of any of the above, with :
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT)
alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)
Tried to use a service instead of BroadcastReceiver. Also tried on a different process.
Tried making the app be ignored from the battery optimization (didn't help), but since other apps don't need it, I shouldn't use it either.
Tried using this:
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
- Tried having a service that will have a trigger of onTaskRemoved , to re-schedule the alarm there, but this also didn't help (the service worked fine though).
As for Google's Clock app, I didn't see anything special about it except that it shows a notification before being triggered, and I also don't see it in the "not optimized" section of the battery-optimization settings screen.
Seeing that this seems like a bug, I reported about this here, including a sample project and video to show the issue.
I've checked on multiple versions of the emulator, and it seems that this behavior started from API 27 (Android 8.1 - Oreo). Looking at the docs, I don't see AlarmManager being mentioned, but instead, it was written about various background work.
The questions
How do we set something to be triggered at a relatively exact time nowadays?
How come the above solutions don't work anymore? Am I missing anything? Permission? Maybe I'm supposed to use a Worker instead? But then wouldn't it mean that it might not trigger on time at all?
How does Google "Clock" app overcome all of this, and triggers anyway on the exact time, always, even if it was triggered just a minute ago? Is it only because it's a system app? What if it gets installed as a user app, on a device that doesn't have it built-in?
If you say that it's because it's a system app, I've found another app that can trigger an alarm twice in 2 minutes, here, though I think it might use a foreground service sometimes.
EDIT: made a tiny Github repository to try ideas on, here.
EDIT: finally found a sample that is both open-sourced and doesn't have this issue. Sadly it's very complex and I still try to figure out what makes it so different (and what's the minimal code that I should add to my POC) that lets its alarms stay scheduled after removing the app from the recent tasks
See Question&Answers more detail:
os