【问题标题】:AlarmManager does not always execute BroadcastReceiverAlarmManager 并不总是执行 BroadcastReceiver
【发布时间】:2015-11-05 10:38:40
【问题描述】:

所以我有一个 BroadcastReceiver 和 AlarmManager。

假设我这样创建 Pending Intents:

Intent i;
i = new Intent(context, MyReceiver.class);
i.setAction(MyReceiver.ACTION_1);
i.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
pendingIntent1 = PendingIntent.getBroadcast(context, 1, i, PendingIntent.FLAG_UPDATE_CURRENT);

i = new Intent(context, MyReceiver.class);
i.setAction(MyReceiver.ACTION_2);
i.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
pendingIntent2 = PendingIntent.getBroadcast(context, 2, i, PendingIntent.FLAG_UPDATE_CURRENT);

并像这样安排警报:

now = SystemClock.elapsedRealtime();
long time1 = now + 10 * 1000;
long time2 = time1 + 60 * 1000;

am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, time1, pendingIntent1);
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, time2, pendingIntent2);

我现在发现我的广播接收器非常可靠地获得了ACTION_1 的广播,而ACTION_2 经常没有被传送。所以onReceive 很少或从不以持有动作ACTION_2 的意图执行。怎么会这样?我想,*_WAKEUP 确保无论如何都在传送广播?

[2015 年 9 月 15 日更新] - 出于测试目的,我试图在我的onReceive 方法中打印出一条日志消息。还是行不通。 - 我现在尝试在 AlarmManager 上使用 setExact。还是行不通。 - 我什至尝试过使用WakefulBroadcastReceiver。还是行不通。 - 但是,我发现设备在电池充电状态下可以可靠地唤醒。 什么可能导致这个问题?我在各处都读到,如果警报管理器通过未决意图触发广播接收器,则保证会执行(并且不会做太多事情)在onReceive)。我的手机上是否有一些我无法真正解决的激进节能政策(如果没有获得长唤醒锁,请参阅 cmets)?

[2015 年 9 月 19 日更新] 我刚刚在 Google Play 上测试了一些闹钟应用程序 (https://play.google.com/store/apps/details?id=com.alarmclock.xtreme.free),但它也不能可靠地唤醒手机。我想,这真的是一个错误,而不是我的错。我想我会坚持使用唤醒锁定解决方案。

【问题讨论】:

  • showAt 的值是多少?
  • 抱歉,已修复。它本来是time1。该应用程序基本上是关于在屏幕关闭一段时间(此处为 10 秒)后显示一个对话框(在透明活动中)并再次隐藏它,如果屏幕关闭时间过长(此处为 10+60=70 秒)。
  • 我现在通过获取唤醒锁解决了我的问题,如下所示:wakeLock.acquire(time2 - now + 5000);(保持手机唤醒 - 在上面的示例中 - 70 秒加上 5 秒作为缓冲区) - 但是,我觉得这是一个相当老套的解决方案。你怎么看?它是合法的还是太脏了,可能会导致问题?
  • 您测试的是哪部手机?索尼机型默认启用积极的省电模式,可以阻止服务唤醒。
  • 是的,它是 Sony Xperia Z1 Compact...我猜,手动获取超时唤醒锁的方法是 - 尽管它很脏 - 那么最可靠的解决方案?

标签: android broadcastreceiver alarmmanager wakelock


【解决方案1】:

我遇到了同样的问题,我找到的解决方案是仅通过操作字符串创建意图,并在清单中注册该接收器操作。尝试将意图更改为这样的:

i = new Intent("com.app.ACTION_ONE");

然后在您的清单文件中将以下内容添加到您的接收器:

<intent-filter>
    <action android:name="com.app.ACTION_ONE" />
</intent-filter>

我的猜测是,如果您没有向接收者注册至少 1 项操作,他就会在应用程序终止时死去。

希望它有效,祝你好运。

【讨论】:

  • 还是不行。 :/另一个旁注:如果我安排第一次广播,例如关闭屏幕 3 秒后,它通常会在再次打开屏幕后执行(但是在我已经等了 10 秒之后)。
【解决方案2】:

您是否注册了 BroadcastReceiver(或从 Manifest)?

 registerReceiver(new MyReceiver(), new IntentFilter(MyReceiver.class.getName()));

我上次使用的方法几乎相同,但使用了AlarmManager.RTC_WAKEUP - 如果您不想将设备从深度睡眠中唤醒,请使用AlarmManager.RTC

long time = System.currentTimeMillis() + 10*1000;
alarmMgr.set(AlarmManager.RTC_WAKEUP, time, alarmIntent);

alarmIntent 像:

alarmIntent = PendingIntent.getBroadcast(this, 0, new Intent(MyReceiver.class.getName()), PendingIntent.FLAG_CANCEL_CURRENT);

【讨论】:

    【解决方案3】:

    有几件事需要注意:

    1. 您正在使用 AlarmManager.set() 方法,但不保证交货时间,如果您想在准确的时间触发警报,请使用 AlarmManager.setExact()

    2. 你的广播意图对我来说没有意义。它是隐含的,但不应该是广播。这种意图可能是奇怪的 android 行为的原因。如果您想要隐式意图将其直接发送到处理它的服务。在接收器情况下,我建议使用:

      Intent intent = new Intent(Contants.Action1);
      
    3. 如果您的接收器是在清单中定义的,则即使您的应用程序的所有活动都已死亡,即使您的应用程序的所有活动都已失效,如果接收器是动态注册的,它只能在它被创建的线程处于活动状态时接收意图。

    4. 不同版本的 Android 尝试以略有不同的方式优化警报,但目标是同时触发尽可能多的警报以实现更好的电池性能。

    所有这些都可能令人困惑。但是,如果接收者注册正确,则可以保证传递意图。

    【讨论】:

    • 您好,感谢您的回复。 1. 我已经使用setExact 将我的代码更改为Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.KITKAT。 2. 我已经使用new Intent(MyReceiver.ACTION_1); 更改了我的代码。 3.我的接收器当前在清单中定义(虽然我都试过了)。 ~~ 嗯,然而,我的经验是,在这一点上,它们不能保证正确交付。我目前将我的问题归咎于索尼的一些严格的节能政策,但我不确定。另请查看我的编辑:我可以使用警报应用程序重现不当行为。
    【解决方案4】:

    查看索尼设备上“耐力模式”的设置。 尝试禁用耐力模式或将您的应用添加到耐力模式开启时允许的应用白名单。

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多