Receptores de difusión en Xamarin.Android

En esta sección se explica cómo utilizar un receptor de radiodifusión.

Información general sobre el receptor de radiodifusión

Un receptor de difusión es un componente de Android que permite a una aplicación responder a mensajes (un AndroidIntent) que son difundidos por el sistema operativo Android o por una aplicación. Las difusiones siguen un modelo de publicación-suscripción: un evento provoca la publicación de una difusión que es recibida por los componentes interesados en el evento.

Android identifica dos tipos de difusiones:

  • Difusión explícita: este tipo de difusión se dirige a una aplicación concreta. El uso más común de una emisión explícita es iniciar una Actividad. Un ejemplo de difusión explícita es cuando una aplicación necesita marcar un número de teléfono; enviará una intención dirigida a la aplicación Teléfono de Android y transmitirá el número de teléfono que debe marcarse. Android dirigirá entonces la intención a la aplicación Teléfono.
  • Difusión implícita: estas difusiones se envían a todas las aplicaciones del dispositivo. Un ejemplo de emisión implícita es la intención ACTION_POWER_CONNECTED. Esta intención se publica cada vez que Android detecta que la batería del dispositivo se está cargando. Android enrutará esta intención a todas las aplicaciones que se hayan registrado para este evento.

El receptor de difusión es una sub clase del tipo BroadcastReceiver y debe anular el métodoOnReceive. Android se ejecutará OnReceive en el hilo principal, por lo que este método debe ser diseñado para ejecutarse rápidamente. Hay que tener cuidado al generar hilos en OnReceive porque Android puede terminar el proceso cuando finalice el método. Si un receptor de difusión debe realizar un trabajo de larga duración, se recomienda programar un trabajo utilizando el JobScheduler o el Distribuidor de trabajos Firebase. La programación del trabajo con una tarea se tratará en otra guía.

Un filtro de intención se utiliza para registrar un receptor de difusión para que Android pueda enrutar correctamente los mensajes. El filtro de intención puede ser especificado en tiempo de ejecución (esto es a veces referido como un receptor registrado en contexto o como registro dinámico) o puede ser definido estáticamente en el Manifiesto de Android (un receptor registrado en manifiesto). Xamarin.Android proporciona un atributo de C#,IntentFilterAttribute, que registrará estáticamente el filtro de intenciones (esto se tratará con más detalle más adelante en esta guía). A partir de Android 8.0, no es posible que una aplicación se registre estáticamente para una difusión implícita.

La principal diferencia entre el receptor registrado en el manifiesto y el receptor registrado en el contexto es que un receptor registrado en el contexto solo responderá a las difusiones mientras se esté ejecutando una aplicación, mientras que un receptor registrado en el manifiesto puede responder a las difusiones aunque la aplicación no se esté ejecutando.

Existen dos conjuntos de API para administrar un receptor de difusión y enviar difusiones:

  1. Context - La clase Android.Content.Context se puede utilizar para registrar un receptor de difusión que responderá a los eventos de todo el sistema. Context también se utiliza para publicar emisiones para todo el sistema.
  2. LocalBroadcastManager - Se trata de una API que está disponible a través del paquete NuGet de Xamarin Support Library v4. Esta clase se utiliza para mantener aisladas las emisiones y los receptores de emisiones en el contexto de la aplicación que los utiliza. Esta clase puede ser útil para evitar que otras aplicaciones respondan a las difusión solo de la aplicación o enviar mensajes a receptores privados.

Un receptor de difusión no puede mostrar diálogos, y no se recomienda iniciar una actividad desde un receptor de difusión. Si un receptor de difusión debe notificar al usuario, entonces debe publicar una notificación.

No es posible vincular o iniciar un servicio desde un receptor de difusión.

En esta guía se explica cómo crear un receptor de difusión y cómo registrarlo para que pueda recibir emisiones.

Crear un receptor de difusión

Para crear un receptor de difusión en Xamarin.Android, una aplicación debe sub clasificar la clase BroadcastReceiver, adornarla con el BroadcastReceiverAttribute, y anular el método 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");
    }
}

Cuando Xamarin.Android compile la clase, también actualizará AndroidManifest con los metadatos necesarios para registrar el receptor. Para un receptor de difusión registrado estáticamente, la propiedad Enabled debe establecerse en true, de lo contrario Android no será capaz de crear una instancia del receptor.

La propiedad Exported controla si el receptor de difusión puede recibir mensajes de fuera de la aplicación. Si la propiedad no se establece explícitamente, el valor predeterminado de la propiedad lo determina Android en función de si hay algún filtro de intención asociado al receptor de difusión. Si existe al menos un filtro de intención para el receptor de emisión, Android asumirá que la propiedad Exported es true. Si no hay filtros de intención asociados al receptor de difusión, Android asumirá que el valor es false.

El método OnReceive recibe una referencia al Intent que fue enviado al receptor de difusión. Esto hace posible que el emisor de la intención pase valores al receptor de la emisión.

Registro estático de un receptor de difusión con un filtro de intenciones

Cuando un BroadcastReceiver está decorado con el IntentFilterAttribute, Xamarin.Android agregará el elemento <intent-filter> necesario al manifiesto de Android en tiempo de compilación. El siguiente fragmento es un ejemplo de un receptor de difusión que se ejecutará cuando un dispositivo haya terminado de arrancar (si el usuario ha concedido los permisos de Android adecuados):

[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.     
    }
}

Nota:

En Android 8.0 (API 26 y superiores), Google puso limitaciones a lo que las aplicaciones pueden hacer mientras los usuarios no interactúan directamente con ellas. Estas limitaciones afectan a los servicios de fondo y a los receptores de difusión implícitos, como Android.Content.Intent.ActionBootCompleted. Debido a estas limitaciones, es posible que tenga dificultades para registrar un receptor de difusión Boot Completed en las versiones más recientes de Android. Si este es el caso, tenga en cuenta que estas restricciones no se aplican a los servicios en primer plano, que pueden ser llamados desde su receptor de emisión.

También es posible crear un filtro de intención que responda a intentos personalizados. Considere el ejemplo siguiente:

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

Las aplicaciones orientadas a Android 8.0 (nivel de API 26) o superior no pueden registrarse estáticamente para una difusión implícita. Las aplicaciones aún pueden registrarse estáticamente para una difusión explícita. Hay una pequeña lista de emisiones implícitas que están exentas de esta restricción. Estas excepciones se describen en la guía Excepciones de difusión implícitas de la documentación de Android. Las aplicaciones interesadas en las difusiones implícitas deben hacerlo de forma dinámica mediante el método RegisterReceiver. Esto se describe a continuación.

Contexto-Registro de un receptor de radiodifusión

El registro del contexto (también denominado registro dinámico) de un receptor se realiza invocando el método RegisterReceiver, y el receptor de difusión debe anularse con una llamada al método UnregisterReceiver. Para evitar la fuga de recursos, es importante dar de baja al receptor cuando ya no sea relevante para el contexto (la actividad o el servicio). Por ejemplo, un servicio puede emitir una intención para informar a una Actividad de que hay actualizaciones disponibles para ser mostradas al usuario. Cuando se inicie la Actividad, se registrará para esa intención. Cuando la Actividad pasa a segundo plano y deja de ser visible para el usuario, debe anular el registro del receptor porque la interfaz de usuario para mostrar las actualizaciones ya no es visible. El siguiente fragmento de código es un ejemplo de cómo registrar y anular el registro de un receptor de difusión en el contexto de una Actividad:

[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();
    }
}

En el ejemplo anterior, cuando la actividad entra en primer plano, se registrará un receptor de difusión que escuchará una intención personalizada utilizando el método del ciclo de vida OnResume. A medida que la Actividad pasa a un segundo plano, el método OnPause() anulará el registro del receptor.

Publicar una difusión

Se puede publicar una difusión a todas las aplicaciones instaladas en el dispositivo creando un objeto de intención y enviándolo con el método SendBroadcast o SendOrderedBroadcast.

  1. Métodos Context.SendBroadcast: existen varias implementaciones de este método. Estos métodos transmitirán la intención a todo el sistema. Receptores de difusión que recibirán la intención en un orden indeterminado. Esto proporciona una gran flexibilidad, pero significa que es posible que otras aplicaciones se registren y reciban la intención. Esto puede suponer un riesgo potencial para la seguridad. Es posible que las aplicaciones necesiten implementar seguridad adicional para evitar accesos no autorizados. Una posible solución es utilizar el LocalBroadcastManager que solo enviará mensajes dentro del espacio privado de la aplicación. Este fragmento de código es un ejemplo de cómo enviar una intención utilizando uno de los métodos SendBroadcast:

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

    Este fragmento es otro ejemplo de envío de una difusión utilizando el método Intent.SetAction para identificar la acción:

    Intent intent = new Intent();
    intent.SetAction("com.xamarin.example.TEST");
    intent.PutExtra("key", "value");
    SendBroadcast(intent);
    
  2. Context.SendOrderedBroadcast : este método es muy similar a Context.SendBroadcast, con la diferencia de que la intención se publicará de una en una a los receptores, en el orden en que se registraron los receptores.

LocalBroadcastManager

La biblioteca de soporte de Xamarin v4 proporciona una clase de ayuda denominada LocalBroadcastManager. LocalBroadcastManager está pensado para aplicaciones que no desean enviar ni recibir transmisiones de otras aplicaciones del dispositivo. LocalBroadcastManager solo publicará mensajes dentro del contexto de la aplicación, y solo a aquellos receptores de difusión que estén registrados con el LocalBroadcastManager. Este fragmento de código es un ejemplo de registro de un receptor de difusión con LocalBroadcastManager:

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

Otras aplicaciones del dispositivo no pueden recibir los mensajes que se publican con el LocalBroadcastManager. Este fragmento de código muestra cómo despachar una intención utilizando la función 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);