Le app Android possono inviare o ricevere annunci dal sistema Android e da altre app Android, in modo simile al pattern di progettazione publish-subscribe. Queste trasmissioni vengono inviate quando si verifica un evento di interesse. Ad esempio, il sistema Android invia annunci quando si verificano vari eventi di sistema, ad esempio l'avvio del sistema o l'avvio della ricarica del dispositivo. Le app possono anche inviare annunci personalizzati, ad esempio, per notificare ad altre app qualcosa che potrebbero essere di loro interesse (ad esempio, alcuni nuovi dati sono stati scaricati).
Il sistema ottimizza la distribuzione delle trasmissioni per mantenere l'integrità ottimale del sistema. Di conseguenza, i tempi di consegna delle trasmissioni non sono garantiti. Le app che richiedono una comunicazione tra processi a bassa latenza dovrebbero considerare i servizi associati.
Le app possono registrarsi per ricevere trasmissioni specifiche. Quando viene inviato un annuncio, il sistema le indirizza automaticamente alle app in abbonamento per ricevere quel particolare tipo di trasmissione.
In generale, gli annunci possono essere utilizzati come sistema di messaggistica per più app e al di fuori del normale flusso utente. Tuttavia, devi fare attenzione a non abusare dell'opportunità di rispondere alle trasmissioni ed eseguire job in background che possono contribuire a un rallentamento delle prestazioni del sistema.
Informazioni sulle trasmissioni di sistema
Il sistema invia automaticamente le trasmissioni quando si verificano vari eventi di sistema, ad esempio quando il sistema attiva e disattiva la modalità aereo. Le trasmissioni di sistema vengono inviate a tutte le app iscritte per ricevere l'evento.
Il messaggio broadcast è racchiuso in un oggetto Intent
la cui stringa di azione identifica l'evento che si è verificato (ad esempio android.intent.action.AIRPLANE_MODE
). L'intent può anche includere informazioni aggiuntive nel relativo campo aggiuntivo. Ad esempio, l'intent della modalità aereo include un extra booleano che indica se la modalità aereo è attiva o meno.
Per ulteriori informazioni su come leggere gli intent e ottenere la stringa di azione da un intent, consulta Intent e filtri di intent.
Per un elenco completo delle azioni di trasmissione di sistema, consulta il file BROADCAST_ACTIONS.TXT
nell'SDK Android. A ogni azione di trasmissione è associato
un campo costante. Ad esempio, il valore della costante ACTION_AIRPLANE_MODE_CHANGED
è android.intent.action.AIRPLANE_MODE
. La documentazione per ogni azione di trasmissione
è disponibile nel relativo campo della costante associato.
Modifiche alle trasmissioni di sistema
A mano a mano che la piattaforma Android si evolve, cambia periodicamente il comportamento delle trasmissioni di sistema. Tieni presente le seguenti modifiche per supportare tutte le versioni di Android.
Android 14
Mentre le app si trovano nello stato nella cache, la pubblicazione delle trasmissioni è ottimizzata per l'integrità del sistema. Ad esempio, le trasmissioni di sistema meno importanti, come ACTION_SCREEN_ON
, vengono differite quando l'app è in stato memorizzato nella cache. Una volta che l'app passa dallo stato memorizzato nella cache a un ciclo di vita del processo attivo, il sistema eroga eventuali trasmissioni differite.
Le trasmissioni importanti dichiarate nel manifest rimuovono temporaneamente le app dallo stato memorizzato nella cache per la pubblicazione.
Android 9
A partire da Android 9 (livello API 28), la trasmissione di NETWORK_STATE_CHANGED_ACTION
non riceve informazioni sulla posizione dell'utente o sui dati che consentono l'identificazione personale.
Inoltre, se la tua app è installata su un dispositivo con Android 9 o versioni successive, le trasmissioni di sistema dal Wi-Fi non contengono SSID, BSSID, informazioni sulla connessione o risultati di ricerca. Per ottenere queste informazioni, chiama il numero
getConnectionInfo()
.
Android 8.0
A partire da Android 8.0 (livello API 26), il sistema impone ulteriori limitazioni ai ricevitori dichiarati dal file manifest.
Se la tua app ha come target Android 8.0 o versioni successive, non puoi utilizzare il file manifest per dichiarare un ricevitore per la maggior parte delle trasmissioni implicite (trasmissioni che non hanno come target specificatamente la tua app). Puoi comunque utilizzare un ricevitore registrato nel contesto quando l'utente sta utilizzando attivamente la tua app.
Android 7.0
Android 7.0 (livello API 24) e versioni successive non inviano le seguenti trasmissioni di sistema:
Inoltre, le app destinate ad Android 7.0 e versioni successive devono registrare la trasmissione CONNECTIVITY_ACTION
utilizzando registerReceiver(BroadcastReceiver, IntentFilter)
.
La dichiarazione di un destinatario nel manifest non funziona.
Ricezione di annunci
Le app possono ricevere trasmissioni in due modi: tramite ricevitori dichiarati con manifest e ricevitori registrati nel contesto.
Ricevitori dichiarati da manifest
Se dichiari un ricevitore di trasmissione nel file manifest, il sistema avvia la tua app (se non è già in esecuzione) quando viene inviata la trasmissione.
Per dichiarare un ricevitore di trasmissione nel file manifest, procedi nel seguente modo:
Specifica l'elemento
<receiver>
nel file manifest della tua 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>
I filtri per intent specificano le azioni di trasmissione a cui il ricevitore si abbona.
Sottoclasse
BroadcastReceiver
e implementaonReceive(Context, Intent)
. Il ricevitore della trasmissione nell'esempio seguente registra e visualizza i contenuti della trasmissione: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(); } }
Per abilitare l'associazione delle viste, configura viewBinding nel file build.gradle a livello di modulo.
Il gestore di pacchetti di sistema registra il destinatario quando l'app viene installata. Il ricevitore diventa quindi un punto di ingresso separato nella tua app, il che significa che il sistema può avviare l'app e trasmettere la trasmissione se l'app non è in esecuzione.
Il sistema crea un nuovo oggetto componente BroadcastReceiver
per gestire ogni trasmissione ricevuta. Questo oggetto è valido solo per la durata della chiamata a onReceive(Context, Intent)
. Quando il codice viene restituito da questo metodo, il sistema considera il componente non più attivo.
Ricevitori registrati in contesto
I ricevitori registrati in contesto ricevono trasmissioni purché il loro contesto di registrazione sia valido. Ad esempio, se ti registri in un contesto Activity
, ricevi trasmissioni a condizione che l'attività non venga eliminata. Se ti registri con il contesto dell'applicazione, ricevi annunci finché l'app è in esecuzione.
Per registrare un ricevitore con un contesto, segui questi passaggi:
Nel file di build a livello di modulo della tua app, includi la versione 1.9.0 o successive della libreria AndroidX Core:
Trendy
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-beta02" // 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.0.0" // 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-beta02") // 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.0.0") // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12 implementation("androidx.core:core-splashscreen:1.1.0-alpha02") }
Crea un'istanza di
BroadcastReceiver
:Kotlin
val br: BroadcastReceiver = MyBroadcastReceiver()
Java
BroadcastReceiver br = new MyBroadcastReceiver();
Crea un'istanza di
IntentFilter
:Kotlin
val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
Java
IntentFilter filter = new IntentFilter(APP_SPECIFIC_BROADCAST);
Scegli se il ricevitore della trasmissione deve essere esportato e visibile ad altre app sul dispositivo. Se questo ricevitore è in ascolto di trasmissioni inviate dal sistema o da altre app (anche da altre app di tua proprietà), utilizza il flag
RECEIVER_EXPORTED
. Se invece questo ricevitore ascolta solo le trasmissioni inviate dalla tua app, usa il flagRECEIVER_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; }
Registra il destinatario chiamando
registerReceiver()
:Kotlin
ContextCompat.registerReceiver(context, br, filter, receiverFlags)
Java
ContextCompat.registerReceiver(context, br, filter, receiverFlags);
Per interrompere la ricezione degli annunci, chiama il numero
unregisterReceiver(android.content.BroadcastReceiver)
. Assicurati di annullare la registrazione del destinatario quando non ne hai più bisogno o il contesto non è più valido.Fai attenzione a dove registri e annulli la registrazione del destinatario. Ad esempio, se registri un ricevitore in
onCreate(Bundle)
utilizzando il contesto dell'attività, devi annullare la registrazione inonDestroy()
per evitare di perdere il destinatario dal contesto dell'attività. Se registri un ricevitore inonResume()
, devi annullarne la registrazione inonPause()
per evitare di registrarlo più volte (se non vuoi ricevere annunci in pausa, riducendo così inutili costi di gestione del sistema). Non annullare la registrazione inonSaveInstanceState(Bundle)
, perché questa operazione non viene chiamata se l'utente torna nell'elenco della cronologia.
Effetti sullo stato del processo
Il fatto che BroadcastReceiver
sia operativo o meno influisce sul processo contenuto, il che può alterare la probabilità di eliminazione del sistema. Un processo in primo piano esegue il metodo onReceive()
di un destinatario. Il sistema esegue il processo, ma non in caso di utilizzo eccessivo della memoria.
BroadcastRicevir viene disattivato dopo il giorno onReceive()
. Il processo host del destinatario è importante tanto quanto i componenti dell'app. Se questo processo ospita solo un ricevitore dichiarato da manifest (un'occorrenza frequente per le app con cui l'utente non ha mai o non ha interagito di recente), il sistema potrebbe terminarlo dopo il giorno onReceive()
per rendere disponibili le risorse per altri processi più critici.
Di conseguenza, i ricevitori di trasmissioni non devono avviare thread in background a lunga esecuzione.
Il sistema può interrompere il processo in qualsiasi momento dopo il giorno onReceive()
per recuperare memoria, terminando il thread creato. Per mantenere attivo il processo, pianifica una JobService
dal destinatario utilizzando JobScheduler
in modo che il sistema sappia che il processo è ancora in esecuzione.
Panoramica del lavoro in background fornisce ulteriori dettagli.
Invio di annunci in corso...
Android offre tre modi con cui le app possono inviare annunci:
- Il metodo
sendOrderedBroadcast(Intent, String)
invia trasmissioni a un ricevitore alla volta. Quando ogni ricevitore viene eseguito a turno, può propagare un risultato al ricevitore successivo oppure può interrompere completamente la trasmissione in modo che non venga passato ad altri ricevitori. I ricevitori dell'ordine in esecuzione possono essere controllati con l'attributo android:Priority del filtro di intent corrispondente; i ricevitori con la stessa priorità verranno eseguiti in ordine arbitrario. - Il metodo
sendBroadcast(Intent)
invia trasmissioni a tutti i ricevitori in un ordine non definito. Questa è la cosiddetta trasmissione normale. Questo è più efficiente, ma significa che i ricevitori non possono leggere i risultati di altri ricevitori, propagare i dati ricevuti dalla trasmissione o interromperla.
Lo snippet di codice riportato di seguito mostra come inviare una trasmissione creando un intent e chiamando 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);
Il messaggio broadcast è aggregato in un oggetto Intent
.
La stringa di azione dell'intent deve fornire la sintassi del nome del pacchetto Java dell'app e identificare in modo univoco l'evento di trasmissione. Puoi allegare ulteriori informazioni
all'intent con putExtra(String, Bundle)
.
Puoi anche limitare una trasmissione a un insieme di app della stessa organizzazione
chiamando setPackage(String)
nell'intent.
Limitare le trasmissioni con autorizzazioni
Le autorizzazioni consentono di limitare la trasmissione all'insieme di app che dispongono di determinate autorizzazioni. Puoi applicare limitazioni al mittente o al ricevitore di una trasmissione.
Invio con autorizzazioni
Quando chiami sendBroadcast(Intent, String)
o sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
, puoi specificare un parametro di autorizzazione. Solo i destinatari che hanno richiesto questa autorizzazione con il tag
Kotlin
sendBroadcast(Intent(BluetoothDevice.ACTION_FOUND), Manifest.permission.BLUETOOTH_CONNECT)
Java
sendBroadcast(new Intent(BluetoothDevice.ACTION_FOUND), Manifest.permission.BLUETOOTH_CONNECT)
Per ricevere la trasmissione, l'app ricevente deve richiedere l'autorizzazione, come mostrato di seguito:
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
Puoi specificare un'autorizzazione di sistema esistente, come BLUETOOTH_CONNECT
, o definire un'autorizzazione personalizzata con l'elemento
<permission>
. Per informazioni sulle autorizzazioni e sulla sicurezza in generale, vedi Autorizzazioni di sistema.
Ricezione con autorizzazioni
Se specifichi un parametro di autorizzazione durante la registrazione di un ricevitore di trasmissione (con registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
o nel tag <receiver>
nel file manifest), solo le emittenti che hanno richiesto l'autorizzazione con il tag <uses-permission>
nel file manifest (e successivamente le hanno ottenuto l'autorizzazione se è pericolosa) possono inviare un intent al destinatario.
Ad esempio, supponiamo che l'app di destinazione abbia un ricevitore dichiarato con manifest, come mostrato di seguito:
<receiver android:name=".MyBroadcastReceiver"
android:permission="android.permission.BLUETOOTH_CONNECT">
<intent-filter>
<action android:name="android.intent.action.ACTION_FOUND"/>
</intent-filter>
</receiver>
Oppure l'app ricevente ha un ricevitore registrato in base al contesto, come mostrato di seguito:
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 );
Quindi, per poter inviare annunci a questi ricevitori, l'app di invio deve richiedere l'autorizzazione come mostrato di seguito:
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
Considerazioni sulla sicurezza e best practice
Ecco alcune considerazioni sulla sicurezza e best practice per l'invio e la ricezione di broadcast:
Se molte app si sono registrate per ricevere la stessa trasmissione nel file manifest, il sistema potrebbe avviare molte app, con un impatto significativo sia sulle prestazioni del dispositivo sia sull'esperienza utente. Per evitare che ciò accada, preferisci utilizzare la registrazione del contesto piuttosto che la dichiarazione del file manifest. A volte, il sistema Android stesso impone l'uso dei ricevitori registrati in contesto. Ad esempio, la trasmissione
CONNECTIVITY_ACTION
viene inviata solo a ricevitori registrati in contesto.Non trasmettere informazioni sensibili utilizzando un intent implicito. Le informazioni possono essere lette da qualsiasi app che si registra per ricevere la trasmissione. Esistono tre modi per controllare chi può ricevere i tuoi annunci:
- Puoi specificare un'autorizzazione quando invii un annuncio.
- In Android 4.0 e versioni successive, puoi specificare un pacchetto con
setPackage(String)
quando invii una trasmissione. Il sistema limita la trasmissione all'insieme di app che corrispondono al pacchetto.
Quando registri un destinatario, qualsiasi app può inviare trasmissioni potenzialmente dannose al destinatario dell'app. Esistono diversi modi per limitare le trasmissioni ricevute dalla tua app:
- Puoi specificare un'autorizzazione durante la registrazione di un ricevitore di trasmissione.
- Per i ricevitori dichiarati dal manifest, puoi impostare l'attributo android:exported su "false" nel manifest. Il destinatario non riceve trasmissioni da sorgenti esterne all'app.
Lo spazio dei nomi per le azioni di trasmissione è globale. Assicurati che i nomi delle azioni e altre stringhe siano scritti in uno spazio dei nomi di tua proprietà, altrimenti potresti entrare inavvertitamente in conflitto con altre app.
Poiché il metodo
onReceive(Context, Intent)
di un destinatario viene eseguito nel thread principale, dovrebbe essere eseguito e restituito rapidamente. Se devi eseguire operazioni a lunga esecuzione, presta attenzione alla creazione di thread o all'avvio di servizi in background perché il sistema può terminare l'intero processo dopo il ritorno dionReceive()
. Per ulteriori informazioni, consulta Effetto sullo stato del processo Per eseguire un lavoro a lunga durata, consigliamo di:- Chiamata a
goAsync()
nel metodoonReceive()
del destinatario e passaggio diBroadcastReceiver.PendingResult
a un thread in background. In questo modo la trasmissione rimane attiva dopo il ritorno daonReceive()
. Tuttavia, anche con questo approccio il sistema si aspetta di terminare la trasmissione molto rapidamente (meno di 10 secondi). Ti permette di spostare il lavoro in un altro thread per evitare di modificare il thread principale. - Programmazione di un job con
JobScheduler
. Per ulteriori informazioni, consulta Pianificazione intelligente dei job.
- Chiamata a
Non avviare attività da ricevitori perché l'esperienza utente è fastidiosa, soprattutto se c'è più di un ricevitore. In alternativa, valuta la possibilità di visualizzare una notifica.