Xamarin.Android 中的广播接收器

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

广播接收器概述

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

Android 标识两种类型的广播:

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

广播接收器是 类型的子类 BroadcastReceiver ,它必须重写 OnReceive 方法。 Android 将在main线程上执行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 将假定 属性 Exportedtrue。 如果没有与广播接收器关联的意向筛选器,则 Android 将假定值为 false

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

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

BroadcastReceiver使用 IntentFilterAttribute修饰时,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 执行此操作。 接下来将对此进行介绍。

Context-Registering广播接收器

上下文注册 (也称为接收方的动态注册) 通过调用 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() 方法将注销接收方。

发布广播

广播可以发布到设备上安装的所有应用,以创建 Intent 对象并使用 或 SendOrderedBroadcast 方法调度它SendBroadcast

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