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)

Zordid
  • 10,451
  • 11
  • 42
  • 58

3 Answers3

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... :(

Zordid
  • 10,451
  • 11
  • 42
  • 58
  • Nice! How long does a BroadcastReceiver last before the app will go ANR?You said briefly -- basically, how brief?? Thanks! – app-dev Feb 21 '18 at 18:01
  • 1
    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 perfectly – Ahmed Awad Mar 19 '18 at 09:27
  • @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? – caw May 07 '19 at 13:58
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+.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 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() ? – Zordid Jul 11 '17 at 15:16
  • 2
    @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+. – CommonsWare Jul 11 '17 at 15:21
  • Is technique to run a small task within a fixed internal by using `PendingIntent.getBroadcast` + `AlarmManager` still allowable in Android O ? – Cheok Yan Cheng Aug 22 '17 at 19:16
  • @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. – CommonsWare Aug 22 '17 at 19:23
  • 3
    As I saw some devs facing problem in executing alarm task. https://stackoverflow.com/questions/45815899/localnotification-with-alarmmanager-and-broadcastreceiver-not-firing-up-in-andro Wondering is it because they didn't start the intent explicitly? – Cheok Yan Cheng Aug 22 '17 at 19:28
  • @CheokYanCheng: I agree with your assessment. My guess is that the problem is with the implicit `Intent`. – CommonsWare Aug 22 '17 at 19:29
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.

  • 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 solution – Herve B Nov 16 '18 at 09:08