Receptores de transmissão no Xamarin.Android

Esta seção discute como usar um Receptor de Transmissão.

Visão geral do receptor de difusão

Um receptor de difusão é um componente Android que permite que um aplicativo responda a mensagens (um Android Intent) que são transmitidas pelo sistema operacional Android ou por um aplicativo. As transmissões seguem um modelo de publicação e assinatura – um evento faz com que uma transmissão seja publicada e recebida pelos componentes interessados no evento.

O Android identifica dois tipos de transmissões:

  • Transmissão explícita – esses tipos de transmissões são direcionados a um aplicativo específico. O uso mais comum de uma transmissão explícita é iniciar uma Atividade. Um exemplo de uma transmissão explícita quando um aplicativo precisa discar um número de telefone; ele enviará uma Intenção direcionada ao aplicativo Telefone no Android e transmitirá o número de telefone a ser discado. Em seguida, o Android roteará a intenção para o aplicativo Telefone.
  • Transmissão implícita – essas transmissões são enviadas para todos os aplicativos no dispositivo. Um exemplo de uma transmissão implícita é a ACTION_POWER_CONNECTED intenção. Essa intenção é publicada sempre que o Android detecta que a bateria no dispositivo está carregando. O Android roteará essa intenção para todos os aplicativos que se registraram para esse evento.

O receptor de difusão é uma subclasse do BroadcastReceiver tipo e deve substituir o OnReceive método . O Android será executado OnReceive no thread main, portanto, esse método deve ser projetado para ser executado rapidamente. Deve-se tomar cuidado ao gerar threads no OnReceive porque o Android pode encerrar o processo quando o método for concluído. Se um receptor de difusão precisar executar um trabalho de execução prolongada, é recomendável agendar um trabalho usando o JobScheduler ou o Firebase Job Dispatcher. O agendamento do trabalho com um trabalho será discutido em um guia separado.

Um filtro de intenção é usado para registrar um receptor de difusão para que o Android possa rotear corretamente as mensagens. O filtro de intenção pode ser especificado em runtime (às vezes é chamado de receptor registrado por contexto ou como registro dinâmico) ou pode ser definido estaticamente no Manifesto do Android (um receptor registrado em manifesto). O Xamarin.Android fornece um atributo C#, IntentFilterAttribute, que registrará estaticamente o filtro de intenção (isso será discutido mais detalhadamente mais adiante neste guia). A partir do Android 8.0, não é possível que um aplicativo se registre estaticamente para uma transmissão implícita.

A principal diferença entre o receptor registrado em manifesto e o receptor registrado no contexto é que um receptor registrado em contexto só responderá às transmissões enquanto um aplicativo estiver em execução, enquanto um receptor registrado em manifesto pode responder às transmissões, mesmo que o aplicativo possa não estar em execução.

Há dois conjuntos de APIs para gerenciar um receptor de transmissão e enviar transmissões:

  1. Context – A Android.Content.Context classe pode ser usada para registrar um receptor de transmissão que responderá a eventos em todo o sistema. O Context também é usado para publicar transmissões em todo o sistema.
  2. LocalBroadcastManager – Essa é uma API que está disponível por meio do pacote NuGet da Biblioteca de Suporte do Xamarin v4. Essa classe é usada para manter transmissões e receptores de difusão isolados no contexto do aplicativo que os está usando. Essa classe pode ser útil para impedir que outros aplicativos respondam a transmissões somente de aplicativos ou enviem mensagens para receptores privados.

Um receptor de transmissão pode não exibir caixas de diálogo e é altamente desencorajado iniciar uma atividade de dentro de um receptor de transmissão. Se um receptor de difusão precisar notificar o usuário, ele deverá publicar uma notificação.

Não é possível associar ou iniciar um serviço de dentro de um receptor de transmissão.

Este guia abordará como criar um receptor de transmissão e como registrá-lo para que ele possa receber transmissões.

Criando um receptor de difusão

Para criar um receptor de difusão no Xamarin.Android, um aplicativo deve subclasse a BroadcastReceiver classe , adorná-la com o BroadcastReceiverAttributee substituir o OnReceive método :

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

Quando o Xamarin.Android compilar a classe, ele também atualizará o AndroidManifest com os metadados necessários para registrar o receptor. Para um receptor de transmissão registrado estaticamente, o Enabled deve ser definido corretamente como true, caso contrário, o Android não poderá criar uma instância do receptor.

A Exported propriedade controla se o receptor de difusão pode receber mensagens de fora do aplicativo. Se a propriedade não estiver definida explicitamente, o valor padrão da propriedade será determinado pelo Android com base em se houver filtros de intenção associados ao receptor de difusão. Se houver pelo menos um filtro de intenção para o receptor de difusão, o Android assumirá que a Exported propriedade é true. Se não houver filtros de intenção associados ao receptor de transmissão, o Android assumirá que o valor é false.

O OnReceive método recebe uma referência ao Intent que foi enviado para o receptor de transmissão. Isso possibilita que o remetente da intenção passe valores para o receptor de difusão.

Registrando estaticamente um receptor de difusão com um filtro de intenção

Quando um BroadcastReceiver é decorado com o , o IntentFilterAttributeXamarin.Android adicionará o elemento necessário <intent-filter> ao manifesto do Android em tempo de compilação. O snippet a seguir é um exemplo de um receptor de transmissão que será executado quando um dispositivo tiver terminado de inicializar (se as permissões apropriadas do Android foram concedidas pelo usuário):

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

Observação

No Android 8.0 (API 26 e superior), o Google colocou limitações sobre o que os aplicativos podem fazer enquanto os usuários não estão interagindo diretamente com eles. Essas limitações afetam os serviços em segundo plano e os receptores de difusão implícitos, como Android.Content.Intent.ActionBootCompleted. Devido a essas limitações, você pode ter dificuldades para registrar um Boot Completed receptor de transmissão em versões mais recentes do Android. Se esse for o caso, observe que essas restrições não se aplicam a serviços em primeiro plano, que podem ser chamados do receptor de transmissão.

Também é possível criar um filtro de intenção que responderá a intenções personalizadas. Considere o seguinte exemplo:

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

Aplicativos direcionados ao Android 8.0 (nível de API 26) ou superior podem não se registrar estaticamente para uma transmissão implícita. Os aplicativos ainda podem se registrar estaticamente para uma transmissão explícita. Há uma pequena lista de transmissões implícitas isentas dessa restrição. Essas exceções são descritas no guia Exceções de Transmissão Implícita na documentação do Android. Os aplicativos interessados em transmissões implícitas devem fazer isso dinamicamente usando o RegisterReceiver método . Isso é descrito a seguir.

Context-Registering um receptor de transmissão

O registro de contexto (também conhecido como registro dinâmico) de um receptor é executado invocando o RegisterReceiver método e o receptor de difusão deve ser cancelado com uma chamada para o UnregisterReceiver método . Para evitar o vazamento de recursos, é importante cancelar o registro do receptor quando ele não for mais relevante para o contexto (atividade ou serviço). Por exemplo, um serviço pode transmitir uma intenção de informar a uma Atividade que as atualizações estão disponíveis para serem exibidas para o usuário. Quando a Atividade é iniciada, ela se registraria para essas Intenções. Quando a Atividade é movida para o plano de fundo e não está mais visível para o usuário, ela deve cancelar o registro do receptor porque a interface do usuário para exibir as atualizações não está mais visível. O snippet de código a seguir é um exemplo de como registrar e cancelar o registro de um receptor de transmissão no contexto de uma Atividade:

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

No exemplo anterior, quando a Atividade entrar em primeiro plano, ela registrará um receptor de difusão que escutará uma intenção personalizada usando o método de OnResume ciclo de vida. À medida que a Atividade se move para o plano de fundo, o OnPause() método cancelará o registro do receptor.

Publicando uma transmissão

Uma transmissão pode ser publicada em todos os aplicativos instalados no dispositivo criando um objeto Intent e expedindo-o com o SendBroadcastSendOrderedBroadcast método ou .

  1. Métodos Context.SendBroadcast – há várias implementações desse método. Esses métodos transmitirão a intenção para todo o sistema. Transmitir receptores que receberão a intenção em uma ordem indeterminado. Isso fornece muita flexibilidade, mas significa que é possível que outros aplicativos registrem e recebam a intenção. Isso pode representar um risco potencial à segurança. Os aplicativos podem precisar implementar a segurança de adição para impedir o acesso não autorizado. Uma solução possível é usar o LocalBroadcastManager que só enviará mensagens dentro do espaço privado do aplicativo. Este snippet de código é um exemplo de como expedir uma intenção usando um dos SendBroadcast métodos:

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

    Este snippet é outro exemplo de envio de uma transmissão usando o Intent.SetAction método para identificar a ação:

    Intent intent = new Intent();
    intent.SetAction("com.xamarin.example.TEST");
    intent.PutExtra("key", "value");
    SendBroadcast(intent);
    
  2. Context.SendOrderedBroadcast – esse é um método muito semelhante ao Context.SendBroadcast, com a diferença sendo que a intenção será publicada uma no momento para os receptores, na ordem em que os receptores foram registrados.

LocalBroadcastManager

A Biblioteca de Suporte do Xamarin v4 fornece uma classe auxiliar chamada LocalBroadcastManager. O LocalBroadcastManager destina-se a aplicativos que não desejam enviar ou receber transmissões de outros aplicativos no dispositivo. O LocalBroadcastManager publicará apenas mensagens dentro do contexto do aplicativo e somente para os receptores de transmissão registrados com o LocalBroadcastManager. Este snippet de código é um exemplo de registro de um receptor de transmissão com LocalBroadcastManager:

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

Outros aplicativos no dispositivo não podem receber as mensagens publicadas com o LocalBroadcastManager. Este snippet de código mostra como expedir uma Intenção usando o 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);