Android O and background limits prevents simple alarm notification
Asked Answered
G

3

17

My own app uses the exact same technique as shown by Google I/O app of 2016. see source

I need to remind users at a very specific point in time - using a notification.

For this, I use the AlarmManager to wake the device at the correct point in time:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime, pendingIntent);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        am.setExact(AlarmManager.RTC_WAKEUP, alarmTime, pendingIntent);
    } else {
        am.set(AlarmManager.RTC_WAKEUP, alarmTime, pendingIntent);
    }

The pendingIntent is created like this:

    final Intent intent = new Intent(MyAlarmService.ACTION_NOTIFY_RIDE, null, this, MyAlarmService.class);
    pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);

Now, my MyAlarmService class is a simple IntentService handling the wake-up just for the purpose of create a notification for the user.

The message I get in the log is the following:

W/ActivityManager: Background start not allowed: service Intent { act=xxx.xxx.xxx.action.NOTIFY_RIDE flg=0x4 cmp=xxx.xxx.xxx./xxx.xxx.xxx.service.MyAlarmService (has extras) } 

Now, Google's own implementation is obviously broken - even though I do not want to do any heavy background work, I cannot use this technique anymore. But HOW am I supposed to wake up the user at a very specific point in time then? (think of my App as an alarm clock)

Gomuti answered 10/7, 2017 at 15:35 Comment(0)
G
13

The answer to my question is plain and simple:

Instead of using a service to display the notification (as Google does in its IO Schedule app!), use a BroadcastReceiver!

I have no idea why Google did use an IntentService, but now on Android O, this is simply not working anymore due to background execution limits.

A BroadcastReceiver though obviously still can run for a brief moment and display a notification.

Bonus points if somebody could tell me why Google used an IntentService in the first place... this took me ages to figure out, because I thought Google knows what they are doing... :(

Gomuti answered 2/1, 2018 at 23:7 Comment(3)
Nice! How long does a BroadcastReceiver last before the app will go ANR?You said briefly -- basically, how brief?? Thanks!Beera
You can always use the JobIntentService, which uses the scheduler API and its background compatible and it will override doze's restrictions, I've used it in a migration from a background service that handles home screen widgets and its running perfectlyEbon
@AhmedAwad What you’re suggesting is starting a JobIntentService from the BroadcastReceiver, not using the JobIntentService instead of the BroadcastReceiver (again, as with the IntentService before) for the PendingIntent of the alarm, right?Grampus
T
8

But HOW am I supposed to wake up the user at a very specific point in time then? (think of my App as an alarm clock)

First, your code will not be exact. If the device is in Doze mode, I would expect at best +/- 10 minutes. If you want exact timing, and your app really is an alarm clock, use setAlarmClock().

For your existing code, use getForegroundService() instead of getService() on API Level 26+.

Tashia answered 11/7, 2017 at 10:22 Comment(6)
It is an alarm clock. It is reminding you of a bus going to depart in x minutes. That's why I chose this shown way which works perfectly (and still does) in terms of timing. Where should I use getForegroundService() ?Gomuti
@Zordid: "Where should I use getForegroundService() ?" -- in the code in your question, you are using getService() to create the PendingIntent. You would replace that with getForegroundService() on API Level 26+.Tashia
Is technique to run a small task within a fixed internal by using PendingIntent.getBroadcast + AlarmManager still allowable in Android O ?Pollux
@CheokYanCheng: AlarmManager still works, subject to the same Doze mode and app standby limitations that we have been dealing with since Android 6.0. However, on Android 8.0+, your background service started by AlarmManager can only run for ~1 minute before it is stopped.Tashia
As I saw some devs facing problem in executing alarm task. #45816399 Wondering is it because they didn't start the intent explicitly?Pollux
@CheokYanCheng: I agree with your assessment. My guess is that the problem is with the implicit Intent.Tashia
A
0

You can even consider SyncAdapter/ Job Scheduler to run periodically to execute the same business logic. And to set a new Periodic sync you can have on Application Launch Itself for first time.

Arcboutant answered 12/12, 2017 at 11:13 Comment(1)
I'm running into the same problem and I thought about doing the same thing as you but it did not work (maybe because I did some heavy work in the service). As for Google's initial implementation, they used a service (I think) to handle any heavy work that could make the system kill the broadcast receiver. Which is why the implementation does not work in my case. So I'm still looking for a solutionCapitalization

© 2022 - 2024 — McMap. All rights reserved.