Android 当应用程序处于打瞌睡模式时,警报管理器触发的通知不会触发

Android Notifications triggered by Alarm Manager not Firing when App is in Doze Mode

我有以下要求。用户需要能够在我的应用程序中安排定期提醒,每天在准确时间触发推送通知。

这是我希望我最终不会提交的问题之一,因为在编写它时推荐了类似的问题。然而,一些团队成员花了数小时和数小时查看 Android 开发人员文档和 Whosebug,我们似乎离答案还很近,所以我们来了。

如果我创建提醒并将其设置为在未来 5 分钟后触发通知,则通知会正常触发。

我怀疑这可能是 Android P 中引入的节电、唤醒锁等更改引起的问题,因为在将目标 SDK 更新到 28 之前我们没有遇到这个问题。那是我说我不确定这是唯一的问题,但我可以在 Pixel 和 Pixel 3 XL 运行 Android P.

上始终如一地重现该问题

例如,当用户将提醒设置为半夜某个时间时(大概是在用户睡着了,因此不会使用 phone几个小时)。这些提醒永远不会触发。

我目前正在尝试使用警报管理器来完成此操作。

这个问题似乎与另一个使用 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 方法的问题类似,根据 Android 文档 "will be allowed to trigger even if the system is in a low-power idle (a.k.a. doze) mode" 但是这也不成功。

我怀疑这不起作用的原因是当 phone 处于休眠模式时 setExactAndAllowWhileIdle 不会触发,类似于 this question 中表达的问题。这个问题建议使用 Firebase JobDispatcher,但由于这是一个内部通知,我需要在有或没有网络连接的情况下触发通知,这似乎消除了 Firebase JobDispatcher 作为一个选项。这个问题还表明,一旦 phone 退出打瞌睡模式,用户就会收到通知,但我们永远不会收到通知,他们似乎因为找不到更好的术语而迷路了。

我已经为我的 AndroidManifest.xml 添加了唤醒锁权限:

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

这是我的接收器在 AndroidManifest.xml

中的注册方式
<receiver android:name="com.myapp.receiver.AlarmReceiver">
    </receiver>

这是我当前的实现:

处理通知的待定​​意向

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

我随后调用了一个方法"createAlarm"如下

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

创建警报

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);
        }
    }
}

是的,你是对的。 现在,Google 建议使用 WorkManager 而不是 AlarmManager。 AlarmManager 在他的工作中有很多限制。您可以找到更多信息 here (doze mode)
此外,像素 phone 有一个 bug 和 alarmManager

我遇到过类似的问题。我试过工作经理,但结果是一样的。当 phone 被锁定并处于打瞌睡模式时,不会触发事件。

但是要在准确的时间触发事件,您只需要使用警报管理器。它甚至可以在打瞌睡模式下工作。

注册闹钟管理器的方法(使用设置的准确时间而不是重复)

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);
}

报警广播接收器

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

清单声明

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

您还必须手动处理 重启。每次重新启动后,注册的警报都会被清除。因此,为 Android 重新启动注册一个广播接收器,并在其中再次注册您的警报。

您将无法 运行 在 Oreo 中长期 运行ning 后台服务,因为行为发生了变化,现在 Oreo 优化系统内存、电池等,它会杀死后台服务,以解决你的问题你应该使用前台服务。

查看后台执行限制https://developer.android.com/about/versions/oreo/android-8.0-changes

我的一个建议,如果你可以使用 FCM,那就去吧,因为像微信、Facebook 这样的应用程序使用它来传递通知,他们不会遇到任何问题...

希望这有助于理解问题....

This is the library 对我有用。

添加意图标志FLAG_RECEIVER_FOREGROUND

https://developer.android.com/reference/android/content/Intent#FLAG_RECEIVER_FOREGROUND 在调用广播接收器之前应该做这个技巧

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);