当应用程序处于打盹模式时,警报管理器触发的 Android 通知未触发

Posted

技术标签:

【中文标题】当应用程序处于打盹模式时,警报管理器触发的 Android 通知未触发【英文标题】:Android Notifications triggered by Alarm Manager not Firing when App is in Doze Mode 【发布时间】:2019-08-27 16:16:45 【问题描述】:

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

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

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

我怀疑这可能是由 Android P 中引入的电池节省、唤醒锁定等更改引起的问题,因为在将目标 SDK 更新到 28 之前我们没有这个问题。话虽如此,我并不肯定这是唯一的问题,但我可以在运行 Android P 的 Pixel 和 Pixel 3 XL 上始终如一地重现该问题。

当用户将提醒设置为半夜的某个时间(大概是当用户睡着,因此几个小时都不会使用手机时)时,会发生通知未触发的示例。这些提醒永远不会触发。

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

这个问题似乎与另一个使用Alarm Manager's setRepeating 方法的问题相似,我们发现该方法不起作用。我们改为使用警报管理器的 setExactAndAllowWhileIdle 方法。我们还使用警报管理器setAlarmClock 方法尝试了相同的实现,根据 Android 文档“即使系统处于低功耗空闲(又名打瞌睡)模式,也将允许触发该方法”但是这也是不成功的。

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

我已将唤醒锁定权限添加到我的 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);
        
    

【问题讨论】:

出于类似的原因,我将这个库与作业调度程序一起使用github.com/evernote/android-job 这是一个教科书示例,其中包含一个经过深思熟虑和制定的堆栈溢出问题。 【参考方案1】:

添加意图标志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);

【讨论】:

您能否添加任何解释为什么它有效以及为什么我们应该添加它?【参考方案2】:

我也遇到过类似的问题。我与工作经理一起尝试过,但结果是一样的。当手机被锁定并处于打盹模式时,不会触发该事件。

但要在准确的时间触发事件,您只需要使用警报管理器。即使在打盹模式下它也应该可以工作。

报警管理器的注册方法(使用设定的准确时间而不是重复)

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 并在其中再次注册您的警报。

【讨论】:

它在打盹模式下不起作用,即使它应该在我的问题中说明 请看一下我正在使用的实现。它对我来说在打瞌睡模式下工作。谢谢 +1 因此,您提供的代码似乎与我们实现的相匹配。我在最初的问题中没有包含的是我如何在清单声明中声明我的接收器,它与你的不同,但我不确定它是否重要。有这方面的想法吗? 我认为因为长时间延迟可能是 android 不允许触发事件。我认为您应该将事件划分为多个事件。例如,如果您必须在 14 小时后触发事件,而不是尝试将其分成 6-7 个部分。因此,您将在 1-2 小时后触发事件,并在下一小时再次注册警报管理器。这可能有助于在短时间内按预期触发事件。我不确定这些,但你可以试一试。 嘿@pat8719 你找到解决方案了吗?我正在为此挣扎。你能帮我在打瞌睡模式下发出警报吗?谢谢。【参考方案3】:

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

【讨论】:

很高兴了解像素中潜在的 Oreo 错误,但这已发生在非 Oreo 操作系统上,并且也发生在非 Pixel 设备上 WorkManager 可能是某些任务的良好替代品,但它不适合安排必须在准确时间运行的工作。对于这些情况AlarmManager is still recommended. @trixn 检查通知 developer.android.com/reference/android/app/AlarmManager 它不能完全在 19 API 以上工作 此外,WorkManager 是警报、fifebaseService 等的包装器 @Alexander 当然,如果需要的话。如果您需要精确执行,您只需要使用setExactsetWindow。这实际上是笔记中的第二句话。而且 WorkManager 不是警报包装器。该声明的来源?【参考方案4】:

This is the library 对我有用。

【讨论】:

仅链接的答案被认为质量非常低。你能补充一些关于为什么应该使用这个库的细节吗?【参考方案5】:

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

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

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

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

【讨论】:

我们没有使用服务,我们使用的是 alarmManager 和挂起的意图。后台执行限制不应发挥作用。同样正如问题中提到的,我们没有使用 Firebase,因为即使没有可用的互联网连接,我们也会尝试在内部触发通知。此外,我们必须为每个用户创建不同的 firebase 事件,这可能需要用于进行大型基础架构更改。对于似乎不应该发生的问题,这似乎是个坏主意

以上是关于当应用程序处于打盹模式时,警报管理器触发的 Android 通知未触发的主要内容,如果未能解决你的问题,请参考以下文章

Android 警报管理器 setExactAndAllowWhileIdle() 在打盹模式下的 Android 7.0 Nougat 中不起作用

打盹模式停止了定位服务

用于定期后台任务的警报管理器的替代方案

当应用程序处于前台时,UILocalNotification 在通知中心显示警报

如何以编程方式关闭棉花糖设备中特定应用程序的打盹模式

当应用程序处于活动状态时,phonegap 通用推送 ios 警报