Visão geral das transmissões

Os apps Android podem enviar ou receber mensagens de transmissão do sistema Android e de outros apps Android, de maneira semelhante ao padrão de design publicação/assinatura. Essas transmissões são enviadas quando ocorre um evento de interesse. Por exemplo, o sistema Android envia transmissões quando ocorrem vários eventos, como quando o sistema é inicializado ou o dispositivo começa a carregar. Os apps também podem enviar transmissões personalizadas, por exemplo, para notificar outros apps sobre algo que possam ser do interesse deles (por exemplo, alguns dados novos foram transferidos por download).

O sistema otimiza a entrega de transmissões para manter a integridade ideal. Portanto, os tempos de entrega das transmissões não são garantidos. Os apps que precisam de comunicação entre processos de baixa latência precisam considerar serviços vinculados.

Os apps podem se registrar para receber transmissões específicas. Quando uma transmissão é enviada, o sistema a roteia automaticamente para apps que se inscreveram para receber esse tipo específico de transmissão.

De modo geral, as transmissões podem ser usadas como um sistema de mensagens entre apps e fora do fluxo normal do usuário. No entanto, tenha cuidado para não abusar da oportunidade de responder a transmissões e executar jobs em segundo plano que podem contribuir para um desempenho lento do sistema.

Sobre transmissões do sistema

O sistema envia transmissões automaticamente quando ocorrem vários eventos, como quando ele entra e sai do modo avião. As transmissões do sistema são enviadas para todos os apps inscritos para receber o evento.

A própria mensagem de transmissão é unida em um objeto Intent com uma string de ação que identifica o evento que ocorreu (por exemplo, android.intent.action.AIRPLANE_MODE). A intent também pode incluir outras informações agrupadas no campo extra. Por exemplo, a intent do modo avião inclui um booleano extra que indica se o modo está ativado ou não.

Para saber mais sobre como ler intents e extrair a string de ação de uma intent, consulte Intents e filtros de intents.

Para conferir uma lista completa das ações de transmissão do sistema, consulte o arquivo BROADCAST_ACTIONS.TXT no SDK do Android. Cada ação de transmissão tem um campo de constante associado a ela. Por exemplo, o valor da constante ACTION_AIRPLANE_MODE_CHANGED é android.intent.action.AIRPLANE_MODE. A documentação de cada ação de transmissão está disponível no campo de constante associado.

Mudanças em transmissões do sistema

À medida que a plataforma Android evolui, ela muda periodicamente o comportamento das transmissões do sistema. Lembre-se das seguintes mudanças para oferecer compatibilidade com todas as versões do Android.

Android 14

Enquanto os apps estão em um estado em cache, a entrega de transmissão é otimizada para a integridade do sistema. Por exemplo, transmissões menos importantes do sistema, como ACTION_SCREEN_ON, são adiadas enquanto o app está em cache. Quando o app passa do estado em cache para um ciclo de vida de processo ativo, o sistema entrega todas as transmissões adiadas.

As transmissões importantes declaradas no manifesto removem temporariamente os apps do estado em cache para entrega.

Android 9

No Android 9 (nível 28 da API) e versões mais recentes, a transmissão NETWORK_STATE_CHANGED_ACTION não recebe informações sobre a localização do usuário ou dados de identificação pessoal.

Além disso, se o app estiver instalado em um dispositivo com o Android 9 ou versões mais recentes, as transmissões do sistema pelo Wi-Fi não vão conter SSIDs, BSSIDs, informações de conexão nem resultados de verificação. Para acessar essas informações, chame getConnectionInfo().

Android 8.0

No Android 8.0 (nível 26 da API) e versões mais recentes, o sistema impõe outras restrições aos receptores declarados pelo manifesto.

Se o app for destinado ao Android 8.0 ou versões mais recentes, não será possível usar o manifesto para declarar um receptor para a maioria das transmissões implícitas, ou seja, transmissões que não são direcionadas ao app especificamente. Você ainda pode usar um receptor registrado pelo contexto quando o usuário estiver usando o app ativamente.

Android 7.0

O Android 7.0 (nível 24 da API) e versões mais recentes não enviam as seguintes transmissões do sistema:

Além disso, os apps destinados ao Android 7.0 e versões mais recentes precisam registrar a transmissão CONNECTIVITY_ACTION usando registerReceiver(BroadcastReceiver, IntentFilter). A declaração de um receptor no manifesto não funcionará.

Como receber transmissões

Os apps podem receber transmissões de duas maneiras: por receptores declarados pelo manifesto e receptores registrados pelo contexto.

Receptores declarados pelo manifesto

Se você declarar um broadcast receiver no manifesto, o sistema vai iniciar seu app (se ele ainda não estiver em execução) quando a transmissão for enviada.

Para declarar um broadcast receiver no manifesto, siga as seguintes etapas:

  1. Especifique o elemento <receiver> no manifesto do app.

    <!-- If this receiver listens for broadcasts sent from the system or from
         other apps, even other apps that you own, set android:exported to "true". -->
    <receiver android:name=".MyBroadcastReceiver" android:exported="false">
        <intent-filter>
            <action android:name="APP_SPECIFIC_BROADCAST" />
        </intent-filter>
    </receiver>
    

    Os filtros de intent especificam as ações de transmissão em que seu receptor está inscrito.

  2. Coloque BroadcastReceiver em uma subclasse e implemente onReceive(Context, Intent). O broadcast receiver no exemplo abaixo registra e mostra o conteúdo da transmissão:

    Kotlin

    private const val TAG = "MyBroadcastReceiver"
    
    class MyBroadcastReceiver : BroadcastReceiver() {
    
        override fun onReceive(context: Context, intent: Intent) {
            StringBuilder().apply {
                append("Action: ${intent.action}\n")
                append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n")
                toString().also { log ->
                    Log.d(TAG, log)
    
                    val binding = ActivityNameBinding.inflate(layoutInflater)
                    val view = binding.root
                    setContentView(view)
    
                    Snackbar.make(view, log, Snackbar.LENGTH_LONG).show()
                }
            }
        }
    }
    

    Java

    public class MyBroadcastReceiver extends BroadcastReceiver {
            private static final String TAG = "MyBroadcastReceiver";
            @Override
            public void onReceive(Context context, Intent intent) {
                StringBuilder sb = new StringBuilder();
                sb.append("Action: " + intent.getAction() + "\n");
                sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
                String log = sb.toString();
                Log.d(TAG, log);
    
                ActivityNameBinding binding =
                        ActivityNameBinding.inflate(layoutInflater);
                val view = binding.root;
                setContentView(view);
    
                Snackbar.make(view, log, Snackbar.LENGTH_LONG).show();
            }
        }
    

    Para ativar a vinculação de visualizações, configure a viewBinding no arquivo build.gradle no nível do módulo.

O gerenciador de pacotes do sistema registra o receptor quando o app é instalado. O receptor se torna um ponto de entrada separado no app, o que significa que o sistema pode iniciar o app e enviar a transmissão se ele não estiver em execução.

O sistema cria um novo objeto de componente BroadcastReceiver para processar cada transmissão recebida. Esse objeto é válido apenas durante a chamada para onReceive(Context, Intent). Depois que o código retornar desse método, o sistema vai considerar que o componente não está mais ativo.

Receptores registrados pelo contexto

Os receptores registrados pelo contexto recebem transmissões, desde que o contexto de registro seja válido. Por exemplo, se você se registrar em um contexto Activity, vai receber transmissões, desde que a atividade não seja destruída. Se você se registrar com o contexto do aplicativo, receberá transmissões, desde que o app esteja em execução.

Para registrar um receptor com um contexto, siga as seguintes etapas:

  1. No arquivo de build do módulo do app, inclua a versão 1.9.0 ou mais recente da biblioteca AndroidX Core:

    Groovy

    dependencies {
        def core_version = "1.12.0"
    
        // Java language implementation
        implementation "androidx.core:core:$core_version"
        // Kotlin
        implementation "androidx.core:core-ktx:$core_version"
    
        // To use RoleManagerCompat
        implementation "androidx.core:core-role:1.0.0"
    
        // To use the Animator APIs
        implementation "androidx.core:core-animation:1.0.0-rc01"
        // To test the Animator APIs
        androidTestImplementation "androidx.core:core-animation-testing:1.0.0-rc01"
    
        // Optional - To enable APIs that query the performance characteristics of GMS devices.
        implementation "androidx.core:core-performance:1.0.0"
    
        // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google
        implementation "androidx.core:core-google-shortcuts:1.1.0"
    
        // Optional - to support backwards compatibility of RemoteViews
        implementation "androidx.core:core-remoteviews:1.1.0-alpha01"
    
        // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12
        implementation "androidx.core:core-splashscreen:1.1.0-alpha02"
    }
    

    Kotlin

    dependencies {
        val core_version = "1.12.0"
    
        // Java language implementation
        implementation("androidx.core:core:$core_version")
        // Kotlin
        implementation("androidx.core:core-ktx:$core_version")
    
        // To use RoleManagerCompat
        implementation("androidx.core:core-role:1.0.0")
    
        // To use the Animator APIs
        implementation("androidx.core:core-animation:1.0.0-rc01")
        // To test the Animator APIs
        androidTestImplementation("androidx.core:core-animation-testing:1.0.0-rc01")
    
        // Optional - To enable APIs that query the performance characteristics of GMS devices.
        implementation("androidx.core:core-performance:1.0.0")
    
        // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google
        implementation("androidx.core:core-google-shortcuts:1.1.0")
    
        // Optional - to support backwards compatibility of RemoteViews
        implementation("androidx.core:core-remoteviews:1.1.0-alpha01")
    
        // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12
        implementation("androidx.core:core-splashscreen:1.1.0-alpha02")
    }
    
  2. Crie uma instância de BroadcastReceiver:

    Kotlin

    val br: BroadcastReceiver = MyBroadcastReceiver()
    

    Java

    BroadcastReceiver br = new MyBroadcastReceiver();
    
  3. Crie uma instância de IntentFilter:

    Kotlin

    val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
    

    Java

    IntentFilter filter = new IntentFilter(APP_SPECIFIC_BROADCAST);
    
  4. Escolha se o broadcast receiver será exportado e ficará visível para outros apps no dispositivo. Se o receptor estiver ouvindo transmissões enviadas pelo sistema ou por outros apps, mesmo que sejam seus, use a flag RECEIVER_EXPORTED. Se, em vez disso, o receptor estiver ouvindo apenas para transmissões enviadas pelo app, use a flag RECEIVER_NOT_EXPORTED.

    Kotlin

    val listenToBroadcastsFromOtherApps = false
    val receiverFlags = if (listenToBroadcastsFromOtherApps) {
        ContextCompat.RECEIVER_EXPORTED
    } else {
        ContextCompat.RECEIVER_NOT_EXPORTED
    }
    

    Java

    boolean listenToBroadcastsFromOtherApps = false;
    if (listenToBroadcastsFromOtherApps) {
        receiverFlags = ContextCompat.RECEIVER_EXPORTED;
    } else {
        receiverFlags = ContextCompat.RECEIVER_NOT_EXPORTED;
    }
    
  5. Registre o receptor chamando registerReceiver():

    Kotlin

    ContextCompat.registerReceiver(context, br, filter, receiverFlags)
    

    Java

    ContextCompat.registerReceiver(context, br, filter, receiverFlags);
    
  6. Para parar de receber transmissões, chame unregisterReceiver(android.content.BroadcastReceiver). Cancele o registro do receptor quando não precisar mais dele ou o contexto não for mais válido.

    Atente-se ao local em que você registra e cancela o registro do receptor. Por exemplo, se você registrar um receptor em onCreate(Bundle) usando o contexto da atividade, cancele o registro dele em onDestroy() para evitar vazamento do receptor para fora do contexto da atividade. Se você registrar um receptor em onResume(), cancele o registro em onPause() para evitar o registro várias vezes. Se você não quiser receber transmissões quando pausado, o que poderá reduzir a sobrecarga desnecessária do sistema. Não cancele o registro em onSaveInstanceState(Bundle), porque isso não será chamado se o usuário voltar para a pilha do histórico.

Efeitos no estado do processo

Se a BroadcastReceiver está operando ou não, isso afeta o processo contido, o que pode alterar a probabilidade de destruição do sistema. Um processo em primeiro plano executa o método onReceive() de um receptor. O sistema executa o processo, exceto sob pressão extrema de memória.

O BroadcastReceiver é desativado após onReceive(). O processo do host do receptor é tão importante quanto os componentes do app. Se esse processo hospedar apenas um receptor declarado pelo manifesto, que é uma ocorrência frequente para apps com que o usuário nunca interagiu recentemente, o sistema poderá eliminá-lo após onReceive() para disponibilizar recursos para outros processos mais essenciais.

Portanto, os broadcast receivers não devem iniciar linhas de execução em segundo plano de longa duração. O sistema pode interromper o processo a qualquer momento após onReceive() para liberar memória, encerrando a linha de execução criada. Para manter o processo ativo, programe um JobService do receptor usando o JobScheduler para que o sistema saiba que o processo ainda está funcionando. Visão geral do trabalho em segundo plano fornece mais detalhes.

Como enviar transmissões

O Android oferece três maneiras para os apps enviarem transmissões:

  • O método sendOrderedBroadcast(Intent, String) envia transmissões para um receptor por vez. À medida que cada receptor é executado, ele pode propagar um resultado para o próximo receptor ou pode cancelar completamente a transmissão para que ela não seja transmitida para outros receptores. Os receptores de pedidos executados podem ser controlados com o atributo android:priority do filtro de intent correspondente. Os receptores com a mesma prioridade serão executados em uma ordem arbitrária.
  • O método sendBroadcast(Intent) envia transmissões para todos os receptores em uma ordem indefinida. Isso é chamado de transmissão normal. Isso é mais eficiente, mas significa que os receptores não podem ler resultados de outros receptores, propagar dados recebidos da transmissão ou cancelar a transmissão.

O snippet de código abaixo demonstra como enviar uma transmissão criando uma intent e chamando sendBroadcast(Intent).

Kotlin

Intent().also { intent ->
    intent.setAction("com.example.broadcast.MY_NOTIFICATION")
    intent.putExtra("data", "Nothing to see here, move along.")
    sendBroadcast(intent)
}

Java

Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data", "Nothing to see here, move along.");
sendBroadcast(intent);

A mensagem de transmissão é agrupada em um objeto Intent. A string de ação da intent precisa fornecer a sintaxe do nome do pacote Java do app e identificar de maneira exclusiva o evento de transmissão. É possível anexar outras informações à intent usando putExtra(String, Bundle). Também é possível limitar uma transmissão a um conjunto de apps da mesma organização chamando setPackage(String) na intent.

Como restringir transmissões com permissões

Com elas, é possível restringir transmissões ao conjunto de apps que têm determinadas permissões. Você pode impor restrições ao remetente ou ao receptor de uma transmissão.

Como enviar com permissões

Ao chamar sendBroadcast(Intent, String) ou sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle), você pode especificar um parâmetro de permissão. Somente os receptores que solicitaram essa permissão com a tag no manifesto (e que receberam a permissão se ela for perigosa) podem receber a transmissão. Por exemplo, o código a seguir envia uma transmissão:

Kotlin

sendBroadcast(Intent(BluetoothDevice.ACTION_FOUND),
              Manifest.permission.BLUETOOTH_CONNECT)

Java

sendBroadcast(new Intent(BluetoothDevice.ACTION_FOUND),
              Manifest.permission.BLUETOOTH_CONNECT)

Para receber a transmissão, o app de destino precisa solicitar a permissão, conforme mostrado abaixo:

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

É possível especificar uma permissão do sistema já existente, como BLUETOOTH_CONNECT, ou definir uma permissão personalizada com o elemento <permission>. Para informações sobre permissões e segurança em geral, consulte as Permissões do sistema.

Como receber com permissões

Se você especificar um parâmetro de permissão ao registrar um broadcast receiver (com registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) ou na tag <receiver> no manifesto), somente as emissoras que solicitaram a permissão com a tag <uses-permission> no manifesto (e que receberam a permissão se ela for perigosa) poderão enviar uma intent ao receiver.

Por exemplo, suponha que o app de recebimento tenha um receptor declarado pelo manifesto, conforme mostrado abaixo:

<receiver android:name=".MyBroadcastReceiver"
          android:permission="android.permission.BLUETOOTH_CONNECT">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_FOUND"/>
    </intent-filter>
</receiver>

Ou o app de recebimento tenha um receptor registrado pelo contexto, conforme mostrado abaixo:

Kotlin

var filter = IntentFilter(Intent.ACTION_FOUND)
registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null )

Java

IntentFilter filter = new IntentFilter(Intent.ACTION_FOUND);
registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null );

Para enviar transmissões a esses receptores, o app de envio precisa solicitar a permissão, conforme mostrado abaixo:

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

Considerações de segurança e práticas recomendadas

Confira algumas considerações de segurança e práticas recomendadas para enviar e receber transmissões:

  • Se muitos apps se registrarem para receber a mesma transmissão no manifesto, o sistema poderá iniciar vários apps, causando um impacto significativo no desempenho do dispositivo e na experiência do usuário. Para evitar isso, prefira usar o registro de contexto em vez da declaração de manifesto. Às vezes, o próprio sistema Android impõe o uso de receptores registrados por contexto. Por exemplo, a transmissão CONNECTIVITY_ACTION é entregue apenas a receptores registrados pelo contexto.

  • Não transmita informações confidenciais usando um intent implícito. As informações podem ser lidas por qualquer app que se registre para receber a transmissão. Existem três maneiras de controlar quem pode receber suas transmissões:

    • Você pode especificar uma permissão enviando uma transmissão.
    • No Android 4.0 e versões mais recentes, é possível especificar um pacote com setPackage(String) ao enviar uma transmissão. O sistema restringe a transmissão ao conjunto de apps que correspondem ao pacote.
  • Quando você registra um receptor, qualquer app pode enviar transmissões potencialmente maliciosas a ele. Há várias maneiras de limitar as transmissões recebidas pelo app:

    • Você pode especificar uma permissão registrando um broadcast receiver.
    • Para receptores declarados pelo manifesto, você pode definir o atributo android:exported como "false" no manifesto. O receptor não recebe transmissões de fontes externas ao app.
  • O namespace das ações de transmissão é global. Certifique-se de que os nomes de ação e outras strings sejam gravados em um namespace seu. Caso contrário, você pode entrar em conflito inadvertida com outros apps.

  • Como o método onReceive(Context, Intent) de um receptor é executado na linha de execução principal, ele precisa ser executado e retornar rapidamente. Se você precisar realizar um trabalho de longa duração, tenha cuidado ao gerar linhas de execução ou iniciar serviços em segundo plano, porque o sistema pode encerrar todo o processo depois que onReceive() retornar. Para mais informações, consulte Efeito no estado do processo. Para executar trabalhos de longa duração, recomendamos:

    • Chamar goAsync() no método onReceive() do receptor e transmitir BroadcastReceiver.PendingResult para uma linha de execução em segundo plano. Isso mantém a transmissão ativa após retornar de onReceive(). No entanto, mesmo com essa abordagem, o sistema espera que você termine a transmissão muito rapidamente (menos de 10 segundos). Isso permite que você mova o trabalho para outra linha de execução para evitar uma falha na linha principal.
    • programar um trabalho com JobScheduler. Para mais informações, consulte Programação inteligente de jobs.
  • Não inicie atividades em broadcast receivers, porque a experiência do usuário é desagradável, especialmente se houver mais de um receptor. Em vez disso, exiba uma notificação.