Os apps para Android enviam e recebem mensagens de transmissão do sistema Android e de outros apps Android, de modo semelhante ao padrão de design publicação-inscrição. O sistema e os apps geralmente enviam transmissões quando determinados eventos ocorrem. Por exemplo, o sistema Android envia transmissões quando ocorrem vários eventos, como a inicialização do sistema ou o carregamento do dispositivo. Os apps também enviam transmissões personalizadas, por exemplo, para notificar outros apps sobre algo que possa interessar a eles, como o download de novos dados.
Os apps podem se registrar para receber transmissões específicas. Quando uma transmissão é enviada, o sistema a direciona automaticamente para apps que se inscreveram para receber esse tipo específico de transmissão.
No geral, as transmissões podem ser usadas como um sistema de troca de mensagens entre apps e fora do fluxo normal do usuário. No entanto, você precisa ter cuidado para não abusar da oportunidade de responder a transmissões e executar jobs em segundo plano que possam 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. Todos os apps inscritos recebem essas transmissões.
O objeto Intent agrupa a mensagem de transmissão. A string action identifica
o evento que ocorreu, como android.intent.action.AIRPLANE_MODE. O intent também pode incluir outras informações empacotadas no campo extra.
Por exemplo, o intent do modo avião inclui um booleano extra que indica se o modo está ativado ou não.
Para mais informações sobre como ler intents e conseguir a string de ação de um intent, consulte Intents e filtros de intents.
Ações de transmissão do sistema
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 a forma como as transmissões do sistema se comportam. Mantenha as seguintes mudanças em mente para oferecer suporte a todas as versões do Android.
Android 16
No Android 16, a ordem de entrega de transmissão usando o android:priority
atributo ou IntentFilter.setPriority() em diferentes processos
não será garantida. As prioridades de transmissão só são respeitadas no mesmo processo de aplicativo, e não em todos os processos.
Além disso, as prioridades de transmissão são automaticamente restritas ao intervalo
(SYSTEM_LOW_PRIORITY + 1, SYSTEM_HIGH_PRIORITY - 1).
Somente os componentes do sistema podem definir SYSTEM_LOW_PRIORITY, SYSTEM_HIGH_PRIORITY como prioridade de transmissão.
Android 14
Enquanto os apps estão em um estado armazenado em cache, o sistema otimiza a entrega de transmissão
para a integridade do sistema. Por exemplo, o sistema adia transmissões do sistema menos importantes, como ACTION_SCREEN_ON enquanto o app está em um estado armazenado em cache.
Quando o app sai do estado armazenado em cache para um ciclo de vida de processo ativo,
o sistema entrega todas as transmissões adiadas.
As transmissões importantes que são declaradas no manifesto removem temporariamente os apps do estado armazenado em cache para entrega.
Android 9
A partir do Android 9 (nível 28 da API), a NETWORK_STATE_CHANGED_ACTION transmissão não recebe informações sobre o local do usuário nem dados de identificação pessoal.
Se o app estiver instalado em um dispositivo com o Android 9.0 (nível 28 da API) ou mais recente, o sistema não vai incluir SSIDs, BSSIDs, informações de conexão ou resultados de verificação em transmissões Wi-Fi. Para receber essas informações, chame
getConnectionInfo() em vez disso.
Android 8.0
A partir do Android 8.0 (nível 26 da API), o sistema impõe outras restrições aos receptores declarados pelo manifesto.
Se seu app for voltado 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, isto é, transmissões que não segmentam seu app de modo específico. Você ainda pode usar um receptor registrado pelo contexto quando o usuário estiver usando seu app ativamente.
Android 7.0
O Android 7.0 (nível 24 da API) e versões mais recentes não envia as seguintes transmissões do sistema:
Além disso, os apps voltados ao Android 7.0 e versões mais recentes precisam registrar a
CONNECTIVITY_ACTION transmissão usando
registerReceiver(BroadcastReceiver, IntentFilter). A declaração de um receptor no manifesto não funcionará.
Receber transmissões
Os apps podem receber transmissões de duas maneiras: por meio de receptores registrados pelo contexto e por receptores declarados pelo manifesto.
Receptores registrados pelo contexto
Receptores registrados pelo contexto recebem transmissões, contanto que o contexto de registro seja válido. Isso geralmente ocorre entre as chamadas para registerReceiver e
unregisterReceiver. O contexto de registro também se torna inválido quando o sistema destrói o contexto correspondente. Por exemplo, se você se registrar em
um contexto Activity, receberá transmissões enquanto a atividade
permanecer ativa. Se você se registrar com o contexto do aplicativo, receberá transmissões, contanto que o app esteja em execução.
Para registrar um receptor com um contexto, siga as seguintes etapas:
No arquivo de build do módulo do seu app, inclua a versão 1.9.0 ou mais recente da biblioteca AndroidX Core:
Groovy
dependencies { def core_version = "1.18.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.1.0" // To use the Animator APIs implementation "androidx.core:core-animation:1.0.0" // To test the Animator APIs androidTestImplementation "androidx.core:core-animation-testing:1.0.0" // 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" // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12 implementation "androidx.core:core-splashscreen:1.2.0" }
Kotlin
dependencies { val core_version = "1.18.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.1.0") // To use the Animator APIs implementation("androidx.core:core-animation:1.0.0") // To test the Animator APIs androidTestImplementation("androidx.core:core-animation-testing:1.0.0") // 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") // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12 implementation("androidx.core:core-splashscreen:1.2.0") }
Crie uma instância de
BroadcastReceiver:Kotlin
val myBroadcastReceiver = MyBroadcastReceiver()Java
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();Crie uma instância de
IntentFilter:Kotlin
val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")Java
IntentFilter filter = new IntentFilter("com.example.snippets.ACTION_UPDATE_DATA");Escolha se o broadcast receiver precisa ser exportado e visível para outros apps no dispositivo. Se esse receptor estiver ouvindo transmissões enviadas pelo sistema ou por outros apps, mesmo outros apps que você possui, use a flag
RECEIVER_EXPORTED. Se, em vez disso, esse receptor estiver ouvindo apenas transmissões enviadas pelo seu app, use a flagRECEIVER_NOT_EXPORTED.Kotlin
val listenToBroadcastsFromOtherApps = false val receiverFlags = if (listenToBroadcastsFromOtherApps) { ContextCompat.RECEIVER_EXPORTED } else { ContextCompat.RECEIVER_NOT_EXPORTED }Java
boolean listenToBroadcastsFromOtherApps = false; int receiverFlags = listenToBroadcastsFromOtherApps ? ContextCompat.RECEIVER_EXPORTED : ContextCompat.RECEIVER_NOT_EXPORTED;Registre o receptor chamando
registerReceiver():Kotlin
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags)Java
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags);Para parar de receber transmissões, chame
unregisterReceiver(android.content.BroadcastReceiver). Cancele o registro do receptor quando não precisar mais dele ou se o contexto não for mais válido.
Cancelar o registro do broadcast receiver
Enquanto o broadcast receiver estiver registrado, ele vai manter uma referência ao contexto com que você o registrou. Isso pode causar vazamentos se o escopo registrado do receptor exceder o escopo do ciclo de vida do contexto. Por exemplo, isso pode ocorrer quando você registra um receptor em um escopo de atividade, mas esquece de cancelar o registro quando o sistema destrói a atividade. Portanto, sempre cancele o registro do broadcast receiver.
Kotlin
class MyActivity : ComponentActivity() {
private val myBroadcastReceiver = MyBroadcastReceiver()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags)
setContent { MyApp() }
}
override fun onDestroy() {
super.onDestroy()
// When you forget to unregister your receiver here, you're causing a leak!
this.unregisterReceiver(myBroadcastReceiver)
}
}
Java
class MyActivity extends ComponentActivity {
MyBroadcastReceiver myBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags);
// Set content
}
}
Registrar receptores no menor escopo
O broadcast receiver só precisa ser registrado quando você estiver realmente interessado no resultado. Escolha o menor escopo de receptor possível:
LifecycleResumeEffectou métodos de ciclo de vidaonResume/onPauseda atividade: o broadcast receiver só recebe atualizações enquanto o app está no estado retomado.LifecycleStartEffectou métodos de ciclo de vidaonStart/onStopda atividade: o broadcast receiver só recebe atualizações enquanto o app está no estado retomado.DisposableEffect: o broadcast receiver só recebe atualizações enquanto o elemento combinável está na árvore de composição. Esse escopo não está anexado ao escopo do ciclo de vida da atividade. Considere registrar o receptor no contexto do aplicativo. Isso ocorre porque o elemento combinável pode, teoricamente, sobreviver ao escopo do ciclo de vida da atividade e vazar a atividade.- Atividade
onCreate/onDestroy: o broadcast receiver recebe atualizações enquanto a atividade está no estado criado. Cancele o registro emonDestroy()e não emonSaveInstanceState(Bundle), porque isso pode não ser chamado. - Um escopo personalizado: por exemplo, você pode registrar um receptor no escopo
ViewModel, para que ele sobreviva à recriação da atividade. Use o contexto do aplicativo para registrar o receptor, já que ele pode sobreviver ao escopo do ciclo de vida da atividade e vazar a atividade.
Criar elementos combináveis com e sem estado
O Compose tem elementos combináveis com e sem estado. Registrar ou cancelar o registro de um broadcast receiver dentro de um elemento combinável o torna com estado. O elemento combinável não é uma função determinística que renderiza o mesmo conteúdo quando os mesmos parâmetros são transmitidos. O estado interno pode mudar com base em chamadas para o broadcast receiver registrado.
Como prática recomendada no Compose, sugerimos que você divida seus elementos combináveis em versões com e sem estado. Portanto, recomendamos que você eleve a criação do broadcast receiver de um elemento combinável para torná-lo sem estado:
@Composable
fun MyStatefulScreen() {
val myBroadcastReceiver = remember { MyBroadcastReceiver() }
val context = LocalContext.current
LifecycleStartEffect(true) {
// ...
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, flags)
onStopOrDispose { context.unregisterReceiver(myBroadcastReceiver) }
}
MyStatelessScreen()
}
@Composable
fun MyStatelessScreen() {
// Implement your screen
}
Receptores declarados pelo manifesto
Se você declarar um broadcast receiver no manifesto, o sistema vai iniciar seu app quando a transmissão for enviada. Se o app ainda não estiver em execução, o sistema vai iniciá-lo.
Para declarar um broadcast receiver no manifesto, siga as seguintes etapas:
Especifique o
<receiver>elemento no manifesto do seu 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="com.example.snippets.ACTION_UPDATE_DATA" /> </intent-filter> </receiver>Os filtros de intent especificam as ações de transmissão em que seu receptor está inscrito.
Crie uma subclasse de
BroadcastReceivere implementeonReceive(Context, Intent). O broadcast receiver no exemplo a seguir registra e exibe o conteúdo da transmissão:Kotlin
class MyBroadcastReceiver : BroadcastReceiver() { @Inject lateinit var dataRepository: DataRepository override fun onReceive(context: Context, intent: Intent) { if (intent.action == "com.example.snippets.ACTION_UPDATE_DATA") { val data = intent.getStringExtra("com.example.snippets.DATA") ?: "No data" // Do something with the data, for example send it to a data repository: dataRepository.updateData(data) } } }Java
public static class MyBroadcastReceiver extends BroadcastReceiver { @Inject DataRepository dataRepository; @Override public void onReceive(Context context, Intent intent) { if (Objects.equals(intent.getAction(), "com.example.snippets.ACTION_UPDATE_DATA")) { String data = intent.getStringExtra("com.example.snippets.DATA"); // Do something with the data, for example send it to a data repository: if (data != null) { dataRepository.updateData(data); } } } }
O gerenciador de pacotes do sistema registra o receptor quando o app é instalado. Em seguida, o receptor se torna um ponto de entrada separado para seu app, o que significa que o sistema poderá iniciar o app e entregar a transmissão se o app não estiver em execução.
O sistema cria um novo BroadcastReceiver objeto de componente para gerenciar
cada transmissão recebida. Esse objeto é válido apenas pela duração de
a chamada para onReceive(Context, Intent). Depois que o código retorna desse método, o sistema considera que o componente não está mais ativo.
Efeitos no estado do processo
Se o BroadcastReceiver está operando ou não, isso afeta o processo contido
, o que pode alterar a probabilidade de eliminação do sistema. Um processo em primeiro plano
executa o método onReceive() de um receptor. O sistema executa o processo, exceto em casos de extrema pressão da memória.
O sistema desativa o BroadcastReceiver após onReceive().
A importância do processo de host do receptor depende dos componentes do app. Se esse processo hospedar apenas um receptor declarado pelo manifesto, o sistema poderá eliminá-lo após onReceive() para liberar recursos para outros processos mais críticos. Isso é comum para apps com os quais o usuário nunca interagiu ou não interagiu recentemente.
Assim, os broadcast receivers não podem 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 recuperar a 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. A visão geral do trabalho em segundo plano
oferece mais detalhes.
Enviar transmissões
O Android oferece duas maneiras para os apps enviarem transmissões:
- O método
sendOrderedBroadcast(Intent, String)envia transmissões para um receptor de cada vez. À medida que cada receptor é executado, ele pode propagar um resultado para o próximo receptor. Ele também pode interromper completamente a transmissão para que ela não chegue a outros receptores. Você pode controlar a ordem em que os receptores são executados no mesmo processo do app. Para fazer isso, use o atributoandroid:prioritydo filtro de intent correspondente. Os receptores com a mesma prioridade sã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 Normal Broadcast. É mais eficiente, mas significa que os receptores não podem ler resultados de outros receptores, propagar dados recebidos ou abortar a transmissão.
O snippet de código a seguir demonstra como enviar uma transmissão criando um
intent e chamando sendBroadcast(Intent).
Kotlin
val intent = Intent("com.example.snippets.ACTION_UPDATE_DATA").apply {
putExtra("com.example.snippets.DATA", newData)
setPackage("com.example.snippets")
}
context.sendBroadcast(intent)
Java
Intent intent = new Intent("com.example.snippets.ACTION_UPDATE_DATA");
intent.putExtra("com.example.snippets.DATA", newData);
intent.setPackage("com.example.snippets");
context.sendBroadcast(intent);
A mensagem de transmissão é agrupada em um Intent objeto. A string action do intent precisa fornecer a sintaxe do nome do pacote Java do app e identificar exclusivamente o evento de transmissão. Você pode anexar outras informações ao
intent com putExtra(String, Bundle). Você também pode limitar uma transmissão a
um conjunto de apps na mesma organização chamando setPackage(String) em
o intent.
Restringir transmissões com permissões
Você pode restringir transmissões ao conjunto de apps que têm determinadas permissões. É possível aplicar restrições para o remetente ou o receptor de uma transmissão.
Enviar transmissões com permissões
Ao chamar sendBroadcast(Intent, String) ou
sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String,
Bundle)
, você poderá especificar um parâmetro de permissão. Somente receptores que solicitaram que
permissão com a tag <uses-permission> no manifesto poderão receber a
transmissão. Se a permissão for perigosa, você precisará conceder a permissão antes que o receptor possa receber a transmissão. Por exemplo, o código a seguir envia uma transmissão com uma permissão:
Kotlin
context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION)
Java
context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION);
Para receber a transmissão, o app de recebimento precisa solicitar a permissão da seguinte maneira:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Você pode especificar uma permissão de sistema existente, como
BLUETOOTH_CONNECT ou definir uma permissão personalizada com o elemento
<permission>. Para mais informações sobre permissões e segurança em
geral, consulte as Permissões do sistema.
Receber transmissões 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), apenas os transmissores que tiverem
solicitado a permissão com a tag <uses-permission> no
manifesto poderão enviar um intent para o receptor. Se a permissão for perigosa, o transmissor também precisará receber a permissão.
Por exemplo, suponha que seu app de recebimento tenha um receptor declarado pelo manifesto, conforme mostrado abaixo:
<!-- 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=".MyBroadcastReceiverWithPermission"
android:permission="android.permission.ACCESS_COARSE_LOCATION"
android:exported="true">
<intent-filter>
<action android:name="com.example.snippets.ACTION_UPDATE_DATA" />
</intent-filter>
</receiver>
Ou o app de recebimento tenha um receptor registrado pelo contexto, conforme mostrado abaixo:
Kotlin
ContextCompat.registerReceiver(
context, myBroadcastReceiver, filter,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
null, // scheduler that defines thread, null means run on main thread
receiverFlags
)
Java
ContextCompat.registerReceiver(
context, myBroadcastReceiver, filter,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
null, // scheduler that defines thread, null means run on main thread
receiverFlags
);
Em seguida, para poder enviar transmissões para esses receptores, o app de envio precisa solicitar a permissão, conforme mostrado abaixo:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Considerações sobre segurança
Confira algumas considerações de segurança para enviar e receber transmissões:
Se muitos apps tiverem se registrado para receber a mesma transmissão no manifesto, o sistema poderá iniciar vários apps, causando um impacto substancial no desempenho do dispositivo e na experiência do usuário. Para evitar isso, prefira usar o registro de contexto na declaração pelo manifesto. Às vezes, o próprio sistema Android impõe o uso de receptores registrados pelo contexto. Por exemplo, a
CONNECTIVITY_ACTIONtransmissão é entregue apenas a receptores registrados pelo contexto.Não transmita informações confidenciais usando um intent implícito. Qualquer app pode ler as informações se ele se registrar 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 (nível 14 da API) e versões mais recentes, você pode especificar um
pacote com
setPackage(String)enviando 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 maliciosas para o receptor do seu app. 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 "falso" no manifesto. O receptor não recebe transmissões de fontes externas ao app.
O namespace das ações de transmissão é global. Os nomes de ação e outras strings precisam estar escritos em um namespace de sua propriedade. Caso contrário, poderá haver um conflito 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 executar um trabalho de longa duração, tenha cuidado com a geração de linhas de execução ou com a inicialização de serviços em segundo plano, porque o sistema pode eliminar todo o processo depois queonReceive()retornar. Para mais informações, consulte Efeito no estado do processo. Para realizar trabalhos de longa duração, recomendamos:- Chamar
goAsync()no métodoonReceive()do seu receptor e transmitir oBroadcastReceiver.PendingResultpara uma linha de execução em segundo plano. Isso mantém a transmissão ativa após retornar deonReceive(). No entanto, mesmo com essa abordagem, o sistema espera que você termine a transmissão muito rapidamente, isto é, em menos de 10 segundos. Isso permite que você mova o trabalho para outra linha de execução para evitar que a principal apresente falhas; - Programar um job com o
JobScheduler. Para mais informações, consulte Programação inteligente de jobs.
- Chamar
Não inicie atividades a partir de broadcast receivers, porque a experiência do usuário é desagradável, especialmente se houver mais de um receptor. Em vez disso, considere a exibição de uma notificação.