Xamarin.Android 中的广播接收器

本部分讨论如何使用广播接收器。

广播接收器概述

广播接收器 是一个 Android 组件,它允许应用程序响应 Android 操作系统或应用程序广播的消息 (Android Intent)。 广播遵循发布-订阅模型, 某事件会引发对事件感兴趣的组件发布和接收广播。

Android 会标识两种类型的广播:

  • 显式广播–这些类型的广播面向特定应用程序。 显式广播最常见的用途是启动一个活动。 当应用需要拨打电话号码时显式广播的示例;它将调度面向 Android 的 Phone 应用的意图,并传递要拨打的电话号码。 然后,Android 会将该意图路由到 Phone 应用。
  • 隐式广播 - 这些广播将调度到设备上的所有应用。 隐式广播的示例是 ACTION_POWER_CONNECTED 意图。 每次 Android 检测到设备上的电池正在充电时,都会发布此意图。 Android 会将此意图路由到已注册此事件的所有应用。

广播接收器是 BroadcastReceiver 类型的子类,它必须替代 OnReceive 方法。 Android 将在主线程上执行 OnReceive,因此此方法应设计为快速执行。 在 OnReceive 生成线程时应小心,因为 Android 可能会在方法完成时终止进程。 如果广播接收器必须执行长时间运行的工作,则建议使用 JobSchedulerFirebase 作业调度程序来计划作业。 将在单独的指南中讨论计划作业工作。

意图筛选器用于注册广播接收器,以便 Android 可以正确路由消息。 可以在运行时指定意向筛选器(有时称为上下文注册接收器动态注册),也可以在 Android 清单(清单注册接收器)中进行静态定义。 Xamarin.Android 提供了一个 C# 属性 IntentFilterAttribute,它将静态注册意图筛选器(本指南稍后将更详细地就此进行讨论)。 从 Android 8.0 开始,应用程序将无法静态注册隐式广播。

清单注册接收器和上下文注册接收器之间的主要区别在于,上下文注册接收器仅在应用程序运行时响应广播,而清单注册接收器则可以响应广播,即使应用可能未运行也是如此。

有两组 API 用于管理广播接收器和发送广播:

  1. Context - Android.Content.Context 类可用于注册将响应系统范围的事件的广播接收器。 Context 还用于发布系统范围的广播。
  2. LocalBroadcastManager - 这是一个通过 Xamarin 支持库 v4 NuGet 包提供的 API。 此类用于在正在使用广播和广播接收器的应用程序上下文中隔离广播和广播接收器。 此类可用于防止其他应用程序响应仅限应用程序的广播或将消息发送到专用接收器。

广播接收器可能不会显示对话,强烈建议不要从广播接收器中启动活动。 如果广播接收器必须通知用户,则应发布通知。

无法从广播接收器中绑定到或启动服务。

本指南介绍如何创建广播接收器以及如何注册它,以便它可以接收广播。

创建广播接收器

若要在 Xamarin.Android 中创建广播接收器,应用程序应将 BroadcastReceiver 类子类化,使用 BroadcastReceiverAttribute 对其进行修饰,并替代 OnReceive 方法:

[BroadcastReceiver(Enabled = true, Exported = false)]
public class SampleReceiver : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        // Do stuff here.

        String value = intent.GetStringExtra("key");
    }
}

当 Xamarin.Android 编译类时,它还将使用必要的元数据更新 AndroidManifest 以注册接收方。 对于静态注册的广播接收器,Enabled 必须正确设置为 true,否则 Android 将无法创建接收方的实例。

Exported 属性控制广播接收器是否可以从应用程序外部接收消息。 如果未显式设置该属性,则会根据与广播接收器关联的任何意图筛选器由 Android 确定该属性的默认值。 如果广播接收器至少有一个意图筛选器,则 Android 将假定该 Exported 属性为 true。 如果没有与广播接收器关联的意图筛选器,则 Android 将假定该值为 false

OnReceive 方法接收对已调度到广播接收器的 Intent 的引用。 这样一来,意图发送方就可以将值传递给广播接收器。

使用意图筛选器静态注册广播接收器

使用 IntentFilterAttribute 修饰 BroadcastReceiver 时,Xamarin.Android 将在编译时将必要的 <intent-filter> 元素添加到 Android 清单。 以下代码片段是一个广播接收器的示例,该接收器将在设备完成启动时运行(如果用户授予了适当的 Android 权限):

[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { Android.Content.Intent.ActionBootCompleted })]
public class MyBootReceiver : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        // Work that should be done when the device boots.     
    }
}

注意

在 Android 8.0(API 26 及更高版本)中,Google 会施加限制,限制应用程序在用户不与之直接交互时的功能。 这些限制会影响后台服务和隐式广播接收器,例如 Android.Content.Intent.ActionBootCompleted。 由于这些限制,你可能很难在较新版本的 Android 上注册 Boot Completed 广播接收器。 如果是这种情况,请注意,这些限制不适用于可从广播接收器调用的前景服务。

还可以创建将响应自定义意图的意图筛选器。 请考虑以下示例:

[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { "com.xamarin.example.TEST" })]
public class MySampleBroadcastReceiver : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        // Do stuff here
    }
}

面向 Android 8.0(API 级别 26)或更高版本的应用可能不会静态注册隐式广播。 应用仍可能静态注册显式广播。 有一小部分隐式广播不受此限制。 Android 文档中的“隐式广播例外”指南中介绍了这些例外。 对隐式广播感兴趣的应用必须使用 RegisterReceiver 方法动态执行该操作。 接下来将对此进行介绍。

上下文注册广播接收器

接收器的上下文注册(也称为动态注册)是通过调用 RegisterReceiver 方法执行的,并且必须使用对 UnregisterReceiver 方法的调用取消注册广播接收器。 为了防止资源泄露,当接收器不再与上下文(活动或服务)相关时,必须注销接收方。 例如,服务可以广播意图,以通知活动有更新可向用户显示。 活动启动时,它将注册这些意图。 当活动移动到后台并且不再对用户可见时,它应取消注册接收方,因为用于显示更新的 UI 不再可见。 以下代码片段是如何在活动上下文中注册和取消注册广播接收器的示例:

[Activity(Label = "MainActivity", MainLauncher = true, Icon = "@mipmap/icon")]
public class MainActivity: Activity
{
    MySampleBroadcastReceiver receiver;

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        receiver = new MySampleBroadcastReceiver();

        // Code omitted for clarity
    }

    protected override void OnResume()
    {
        base.OnResume();
        RegisterReceiver(receiver, new IntentFilter("com.xamarin.example.TEST"));
        // Code omitted for clarity
    }

    protected override void OnPause()
    {
        UnregisterReceiver(receiver);
        // Code omitted for clarity
        base.OnPause();
    }
}

在前面的示例中,当活动进入前台时,它将注册一个广播接收器,该接收器将使用 OnResume 生命周期方法侦听自定义意图。 当活动进入后台时,OnPause() 方法将取消注册接收方。

发布广播

广播可以发布到设备上安装的所有应用,以便创建意图对象,并使用 SendBroadcastSendOrderedBroadcast 方法调度它。

  1. Context.SendBroadcast 方法–此方法有多个实现。 这些方法会将意图广播到整个系统。 将按不确定顺序接收意图的广播接收器。 这提供了很大的灵活性,但意味着其他应用程序可以注册和接收意图。 这可能会带来潜在的安全风险。 应用程序可能需要实现附加安全性,以防止未经授权的访问。 一种可能的解决方案是使用 LocalBroadcastManager,它仅会调度应用专用空间内的消息。 此代码片段是如何使用 SendBroadcast 方法之一调度意图的一个示例:

    Intent message = new Intent("com.xamarin.example.TEST");
    // If desired, pass some values to the broadcast receiver.
    message.PutExtra("key", "value");
    SendBroadcast(message);
    

    此代码片段是使用 Intent.SetAction 方法标识操作的另一个发送广播的示例:

    Intent intent = new Intent();
    intent.SetAction("com.xamarin.example.TEST");
    intent.PutExtra("key", "value");
    SendBroadcast(intent);
    
  2. Context.SendOrderedBroadcast–此方法与 Context.SendBroadcast 非常相似,区别在于意图将按照接收方注册的顺序,逐一发布给接收方。

LocalBroadcastManager

Xamarin 支持库 v4 提供了名为 LocalBroadcastManager 的帮助程序类。 LocalBroadcastManager 适用于不希望从设备上的其他应用发送或接收广播的应用。 LocalBroadcastManager 只会在应用程序的上下文中发布消息,并且仅会发布到注册到 LocalBroadcastManager 的广播接收器。 此代码片段是向 LocalBroadcastManager 注册广播接收器的示例:

Android.Support.V4.Content.LocalBroadcastManager.GetInstance(this). RegisterReceiver(receiver, new IntentFilter("com.xamarin.example.TEST"));

设备上的其他应用无法接收随 LocalBroadcastManager 一起发布的消息。 此代码片段演示如何使用 LocalBroadcastManager 调度意图:

Intent message = new Intent("com.xamarin.example.TEST");
// If desired, pass some values to the broadcast receiver.
message.PutExtra("key", "value");
Android.Support.V4.Content.LocalBroadcastManager.GetInstance(this).SendBroadcast(message);