Awareness API & Android O using BroadcastReceiver

16

我有一个使用Awareness API设置围栏的安卓应用程序,当耳机插入时可以触发。

我已经按照示例中类似的代码实现了AwarenessFence:https://developers.google.com/awareness/android-api/fence-register

我定义了一个PendingIntent:

PendingIntent.getBroadcast(context, 0, new Intent("my.application.packageFENCE_RECEIVER_ACTION"), 0)

然后在我的AndroidManifest.xml文件中,我有以下内容

<receiver android:name=".fence.FenceDetector$MyFenceReceiver">
<intent-filter>
    <action android:name="my.application.packageFENCE_RECEIVER_ACTION" />
</intent-filter>

这是在清单文件中声明的,因为我希望即使我的应用程序在后台运行时,也能接收广播。

这在Android 7.0及以下版本中都可以正常工作,但当我在Android 8.0上运行时,出现了错误:

BroadcastQueue: Background execution not allowed: receiving Intent { act=my.application.packageFENCE_RECEIVER_ACTION

我猜想这是由于 Android O 上的新后台执行限制所导致的。

有人可以告诉我如何注册一个广播接收器,在运行 API 26 的 Android 设备上,在后台监听 awareness fence 触发器吗?

如果有什么不清楚的地方或者我需要解释些什么,请让我知道。

先谢谢了。

3个回答

13

我现在无法在设备上测试,但根据我所读的内容,限制仅针对隐式广播。也就是说,如果您创建一个显式广播,那么这就足以使其正常工作。

这意味着,不要使用以下代码:

// implicit intent matching action
PendingIntent.getBroadcast(context, 0,
    new Intent("my.application.packageFENCE_RECEIVER_ACTION"), 0)

你这样做:

// explicit intent directly targeting your class
PendingIntent.getBroadcast(context, 0,
    new Intent(context, FenceDetector.MyFenceReceiver.class), 0)

1
已测试并且可行。这应该被接受作为答案。 - Simon
1
你是对的Simon。我刚刚测试了一下,可以确认它正在工作。现在它被接受为答案。 - Michael Franz
请注意,您仍然需要在清单中注册接收器,例如:<receiver android:name=".FenceDetector$MyFenceReceiver"/>。 - Michael Franz
这对Android中的每个PendingIntent都是正确的,@MichaelFranz。系统只会初始化在清单文件中注册的组件。在Android中,只有通过Context.registerReceiver(receiver)方法才能使用非注册的BroadcastReceiver - Budius

6
我进行了一些调查,并偶然发现这篇博客文章,作者是CommonsWare。它阐述了你所面临的问题。
从上面的文章可以看出:对于目标SdkVersion足够高的应用程序,Android O中更有争议的变化之一是禁止隐式广播。
因此,根据这个说法,我认为你的问题与Awareness API无关。相反,它是由Android 8引入的新行为导致的。
不幸的是,目前似乎没有可行的解决方案。再次引用同一篇文章的措辞:
如果您正在接收系统发送的隐式广播(例如ACTION_PACKAGE_ADDED),请将目标SdkVersion保持在25或更低版本,直到我们找到更好的解决方法(希望不涉及轮询)。

希望在不久的将来会有更好的解决方案来解决8的问题。同时,您可以考虑其他选项或降低目标SDK。


这个方法在我开发应用程序时很管用,但是被采纳的答案是更好的解决方案,请查看一下。 - Michael Franz

1

您的理解非常正确。

针对 Android 8.0 或更高版本的应用程序不再能够在其清单中注册隐式广播接收器。隐式广播是指不专门针对该应用程序的广播。

应用程序可以继续在其清单中注册显式广播。

来源

如果您只对插入耳机时设置栅栏感兴趣,您可以使用:

注意: 目前有许多隐式广播被豁免了这个限制。无论应用程序定位到哪个 API 级别,应用程序都可以继续在其清单中为这些广播注册接收器。有关豁免广播的列表,请参阅隐式广播异常

您可以在 Android Manifest 中注册 ACTION_HEADSET_PLUG。在 onReceive 中,您可以选择执行以下操作之一:

  • 启动NotificationManager.startServiceInForeground()以便您可以在后台继续工作。
  • 找到一种方法使用计划任务来复制服务的功能。如果服务对用户没有立即显着的影响,通常可以使用计划任务代替。请参考Job Scheduler
  • 将后台工作推迟到应用程序自然处于前台时再执行。

我建议在需要进行长时间运行的工作时,结合Job SchedulerACTION_HEADSET_PLUG

否则,如果需要进行短时间的工作,则可以在onReceive中使用以下方法:

使用goAsync()标记需要更多时间来完成onReceive()后的工作的BroadcastReceiver。如果您希望在onReceive()中完成的工作足够长,以至于UI线程会错过一帧(>16ms),则这特别有用,使其更适合于后台线程。
您不应该从BroadcastReceiver启动长时间运行的后台线程。在onReceive()之后,系统可以随时终止进程以回收内存,并在此过程中终止在进程中运行的生成线程。为避免这种情况,您应该调用goAsync()(如果您想要更多时间在后台线程中处理广播)或使用JobScheduler从接收器安排JobService,以便系统知道该进程继续执行活动工作。 来源

感谢您的回答,Anurag。然而,我需要使用Awareness API,因为我的围栏不仅包括HeadsetFence,还使用DetectedActivityFence,因此我使用AwarenessFence.and(DetectedActivityFence.during(DetectedActivityFence.IN_VEHICLE), HeadphoneFence.during(HeadphoneState.PLUGGED_IN))结合了两个围栏。 否则,ACTION_HEADSET_PLUG将是完美的选择。 - Michael Franz
这个不会起作用。ACTION_HEADSET_PLUG不能通过清单文件触发,需要在代码中注册。请参考:https://dev59.com/Km025IYBdhLWcg3wCxVN#6366238。因此,这是一种完全无用的方法,因为您不想打开应用程序来检测耳机连接。 - Ferran Negre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接