12

I have the following requirements. A user needs to be able to schedule a recurring reminder in my app that will trigger a push notification at an exact time every day.

This is one of those questions that I hoped I would end up not submitting as similar questions were recommended while writing it. However several team members have spent hours and hours looking through the Android Developer Docs and Stackoverflow and we seem no closer to an answer, so here we are.

If I create a reminder and set it to trigger the notification 5 minutes in the future, the notification fires just fine.

I suspect this may be a problem caused by the changes to battery saving, wake lock, etc introduced in Android P as we did not have this problem prior to updating our target SDK to 28. That being said I am not positive that this is the only problem but I can consistently reproduce the issue on a Pixel and Pixel 3 XL running Android P.

An example where the notification is not firing occurs when for example a user sets the reminder to sometime in the middle of the night (presumably when the user is asleep and thus will not have used the phone for several hours). These reminders are not firing ever.

I am currently trying to accomplish this using the Alarm Manager.

This problem appears to be similar to another question that uses the Alarm Manager's setRepeating method which we have found does not work. We are instead using the Alarm Manager's setExactAndAllowWhileIdle method. We also tried this same implementation with the Alarm Managers setAlarmClock method which according to the Android documentation "will be allowed to trigger even if the system is in a low-power idle (a.k.a. doze) mode" however this was also unsuccessful.

I suspect that the reason this is not working is because setExactAndAllowWhileIdle will not fire when the phone is in doze mode, similar to the problems expressed in this question. This question recommends using Firebase JobDispatcher but as this is an internal notification I am required to fire the notification with or without network connectivity which seems to eliminate the Firebase JobDispatcher as an option. This question also indicated that once the phone exits doze mode the user gets the notification however we are never getting the notification, they seem to be lost for lack of a better term.

I have added the wake lock permission to my AndroidManifest.xml:

<uses-permission android:name="android.permission.WAKE_LOCK" />

Here is how my receiver is registered in the AndroidManifest.xml

<receiver android:name="com.myapp.receiver.AlarmReceiver">
    </receiver>

Here is my current implementation:

Pending Intent for Handling Notifications

Intent i = new Intent(context, ScheduleAllReceiver.class);
    PendingIntent scheduleAllPendingIntent = PendingIntent.getBroadcast(context, SCHEDULER_DAILY_ALL, i, PendingIntent.FLAG_UPDATE_CURRENT);

I subsequently call a method "createAlarm" as follows

createAlarm(context, scheduleAllPendingIntent, calendar.getTimeInMillis());

Creating the Alarms

public static void createAlarm(Context context, PendingIntent pendingIntent, long timeinMilli) {
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

    if(alarmManager != null) {

        if (Build.VERSION.SDK_INT >= 23) {
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeinMilli, pendingIntent);
        } else {
            alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeinMilli, pendingIntent);
        }
    }
}
pat8719
  • 1,580
  • 1
  • 25
  • 45
  • For a similar reason I am using this library with job scheduler https://github.com/evernote/android-job – madlymad Apr 08 '19 at 19:40

5 Answers5

12

Adding an intent Flag FLAG_RECEIVER_FOREGROUND

https://developer.android.com/reference/android/content/Intent#FLAG_RECEIVER_FOREGROUND prior to calling the broadcast receiver should do the trick

Intent intent = new Intent(context, ScheduleAllReceiver.class);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
PendingIntent scheduleAllPendingIntent = PendingIntent.getBroadcast(context, SCHEDULER_DAILY_ALL, intent, PendingIntent.FLAG_UPDATE_CURRENT);
2

I have faced the similar problems. I tried with work manager but the result was same. When phone is locked and is in doze mode the event are not triggered.

But for triggering event at exact time you need to use alarm manager only. It should work even in doze mode.

Method to register alarm manger(use set exact time instead of repeating)

public static void registerAlarm(Context context){
    final int FIVE_MINUTES_IN_MILLI = 300000;
    final int THIRTY_SECOND_IN_MILLI = 30000;
    long launchTime = System.currentTimeMillis() + FIVE_MINUTES_IN_MILLI;
    AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    Intent i = new Intent(context, BroadcastAlarmManger.class);
    PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
        am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, launchTime, pi);
    else am.setExact(AlarmManager.RTC_WAKEUP, launchTime, pi);
    Utility.printLog("timestamp "+launchTime);
}

Broadcast receiver for alarm

public class BroadcastAlarmManger extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //register alarm again
         registerAlarm(context);
        .
        .
        .
        .
        //do your stuff    
    }
}

Manifest declaration

<receiver
        android:name="com.taxiemall.utility.BroadcastAlarmManger"
        android:enabled="true"
        android:exported="true"/>

Also you have to handle Reboot manually. After every reboot registered alarm are cleared. So register a broadcast receiver for Android reboot and register you alarm again inside it.

Royz
  • 145
  • 15
abdul rehman
  • 200
  • 1
  • 8
  • It does not work in doze mode even though it should that was stated in my question – pat8719 Apr 16 '19 at 13:46
  • Please have a look at the implementation which i am using. Its working in doze mode for me. Thank you – abdul rehman Apr 16 '19 at 14:10
  • +1 So the code you provided appears to match what we have implemented. What I failed to include in my original question was how I declared my receiver in the manifest declaration, it differs from yours but I'm not sure if it matters. Any ideas with that regard? – pat8719 Apr 16 '19 at 15:00
  • i think becoz of long delay may be android is not allowing to fire the event. I think you should divide the event into multiple events. Example if you have to fire the event after 14 hours than try to divide that into 6-7 parts. So you will fire the event after 1-2 hour and again register the alarm manager for next hour. This could help as for short period the event are fired as expected. I am not sure about these but you can give it a try. – abdul rehman Apr 16 '19 at 15:51
  • Hey @pat8719 Have you found the solution for it? I'm struggling from it. Can you please help me to fire alarms in doze mode too?. Thanks. – Chirag Prajapati Mar 16 '20 at 11:23
  • @ChiragPrajapati can you explain you problem? – abdul rehman Mar 19 '20 at 11:59
  • @abdulrehman Can I use Foreground service to keep alarm working even in doze mode? – Chirag Prajapati Mar 20 '20 at 09:23
  • @ChiragPrajapati check this posts http://pguardiola.com/blog/darealfragmentation-alarms/ and https://medium.com/mindorks/enable-background-services-in-chinese-roms-32e73dfba1a6 – abdul rehman Mar 31 '20 at 14:15
  • @NarendraSingh you can ask the user to add the app in wishlist apps. In that way it should work. There are code available to ask user to wishlist your app. – abdul rehman Apr 28 '20 at 05:56
1

This is the library that worked for me.

Koop4
  • 3,905
  • 7
  • 24
  • 41
RSauther
  • 11
  • 2
0

Yes, you are right. Now, Google recommends using WorkManager instead of AlarmManager. AlarmManager has many limits in his work. You can find more information here (doze mode)
Also, pixel phone has a bug with alarmManager

Alexander
  • 164
  • 7
  • Good to know about a potential Oreo bug in pixel but this has happened to on non-Oreo OSs and it is also happening non-Pixel devices – pat8719 Apr 16 '19 at 13:52
-1

You would not be able to run background services long running in Oreo as there are behaviour changes, now Oreo to optimise system memory, battery etc, it kills background service, to solve your issue you should use foreground service.

Have a look at Background execution limits https://developer.android.com/about/versions/oreo/android-8.0-changes

A suggestion from me, if you can use FCM then go for it, becasue apps like WeChat, Facebook uses it, to deliver notifications and they don't face any problem...

Hope this helps in understanding the issue....

Abdul Aziz
  • 432
  • 5
  • 11
  • 1
    we are not using services we are using the alarmManager and pending intentets. Background execution limits should not come into play. Also as mentioned in the question we are not using Firebase because we are trying to trigger the notification internally even if no internet connectivity is available. Additionally we'd have to create a different firebase event for each user which could require use to make large infrastructure changes. This seems like a bad idea for a problem that seems like it shouldn't be happening – pat8719 Apr 16 '19 at 13:50