Le app per Android possono inviare o ricevere annunci dal sistema Android e da altre app Android, in modo simile al modello 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, come l'avvio del sistema o la ricarica del dispositivo. Le app possono anche inviare annunci personalizzati, ad esempio, per informare altre app di qualcosa a cui potrebbero essere interessati (ad esempio, che sono stati scaricati alcuni nuovi dati).
Il sistema ottimizza la distribuzione delle trasmissioni per mantenere l'integrità ottimale del sistema. Di conseguenza, i tempi di pubblicazione 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 annunci specifici. Quando viene inviato un broadcast, il sistema indirizza automaticamente gli annunci alle app che si sono iscritte a ricevere quel determinato 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 rallentare le prestazioni del sistema.
Informazioni sulle trasmissioni di sistema
Il sistema invia automaticamente annunci quando si verificano diversi 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 dell'annuncio viene aggregato 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 suo campo aggiuntivo. Ad esempio, l'intent della modalità aereo include un extra booleano che indica se la modalità è attiva o meno.
Per ulteriori informazioni su come leggere gli intent e ottenere la stringa di azione da un intent, consulta la sezione Filtri per intent e 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
La piattaforma Android si evolve, pertanto cambia periodicamente il comportamento delle trasmissioni di sistema. Per supportare tutte le versioni di Android, tieni presente le modifiche che seguono.
Android 14
Mentre le app sono in 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 mentre l'app è in uno stato memorizzato nella cache. Quando l'app passa dallo stato memorizzato nella cache a un ciclo di vita del processo attivo, il sistema invia eventuali trasmissioni differite.
Le trasmissioni importanti dichiarate nel file manifest rimuovono temporaneamente le app dallo stato memorizzato nella cache per la distribuzione.
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 dati che consentono l'identificazione personale.
Inoltre, se l'app è installata su un dispositivo con Android 9 o versioni successive, le trasmissioni di sistema dalla rete Wi-Fi non contengono SSID, BSSID, informazioni sulla connessione o risultati dell'analisi. Per ottenere queste informazioni, chiama invece il numero
getConnectionInfo()
.
Android 8.0
A partire da Android 8.0 (livello API 26), il sistema impone ulteriori limitazioni sui ricevitori dichiarati tramite 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 la tua app specifica). Puoi comunque utilizzare un ricevitore registrato nel contesto quando l'utente utilizza 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 ricevitore nel manifest non funziona.
Ricezione di annunci
Le app possono ricevere trasmissioni in due modi: tramite ricevitori dichiarati tramite manifest e ricevitori registrati in base al contesto.
Ricevitori dichiarati da manifest
Se dichiari un ricevitore di broadcast nel file manifest, il sistema avvia la tua app (se l'app non è già in esecuzione) quando viene inviato la trasmissione.
Per dichiarare un ricevitore di trasmissione nel file manifest, procedi nel seguente modo:
Specifica l'elemento
<receiver>
nel manifest dell'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 di 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 destinatario 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 è attualmente in esecuzione.
Il sistema crea un nuovo oggetto componente BroadcastReceiver
per gestire ogni trasmissione che riceve. 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 base al contesto
I ricevitori registrati in base al 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, riceverai broadcast fino a quando l'app è in esecuzione.
Per registrare un ricevitore con un contesto, segui questi passaggi:
Nel file di build a livello di modulo dell'app, includi la versione 1.9.0 o successive della libreria AndroidX Core:
Groovy
dependencies { def core_version = "1.13.1" // 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" // 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-alpha01" }
Kotlin
dependencies { val core_version = "1.13.1" // 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") // 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-alpha01") }
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 di annunci, chiama il numero
unregisterReceiver(android.content.BroadcastReceiver)
. Assicurati di annullare la registrazione del destinatario quando non ne hai più bisogno o quando il contesto non è più valido.Fai attenzione a dove registri e annulli la registrazione del ricevitore. Ad esempio, se registri un ricevitore in
onCreate(Bundle)
utilizzando il contesto dell'attività, dovresti annullare la registrazione inonDestroy()
per evitare che il ricevitore venga divulgato al di fuori del contesto dell'attività. Se registri un ricevitore inonResume()
, devi annullarne la registrazione inonPause()
per evitare di registrarlo più volte (se non vuoi ricevere trasmissioni quando il dispositivo è in pausa, altrimenti potresti ridurre il sovraccarico del sistema). Non annullare la registrazione inonSaveInstanceState(Bundle)
, perché questa operazione non viene richiamata se l'utente torna nell'elenco della cronologia.
Effetti sullo stato del processo
Il fatto che BroadcastReceiver
sia operativo o meno sul suo 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, tranne in caso di estrema pressione della memoria.
BroadcastRicevir viene disattivato dopo il giorno onReceive()
. Il processo host del destinatario è significativo
solo quanto i componenti dell'app. Se questo processo ospita solo un ricevitore dichiarato da manifest (ricorrenza 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 broadcast 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, interrompendo il thread creato. Per mantenere attivo il processo, programma una JobService
dal ricevitore 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. Mentre 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 per intent corrispondente. I ricevitori con la stessa priorità verranno eseguiti in ordine arbitrario. - Il metodo
sendBroadcast(Intent)
invia trasmissioni a tutti i destinatari in un ordine non definito. si chiama trasmissione normale. Questa operazione è più efficiente, ma significa che i ricevitori non possono leggere i risultati di altri ricevitori, propagare i dati ricevuti dalla trasmissione o interrompere la trasmissione.
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 nella stessa organizzazione chiamando setPackage(String)
tramite l'intent.
Limitazione delle trasmissioni con autorizzazioni
Le autorizzazioni ti consentono di limitare le trasmissioni all'insieme di app che dispongono di determinate autorizzazioni. Puoi applicare limitazioni al mittente o al destinatario 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 l'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 su autorizzazioni e sicurezza in generale, consulta Autorizzazioni di sistema.
Ricezione con autorizzazioni
Se specifichi un parametro di autorizzazione durante la registrazione di un ricevitore
(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 che successivamente hanno ottenuto l'autorizzazione se è
pericolosa) possono inviare un intent al destinatario.
Ad esempio, supponiamo che l'app di destinazione abbia un ricevitore dichiarato da 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 );
Per poter inviare annunci a questi destinatari, l'app di invio deve quindi 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, ciò può causare il lancio di molte app da parte del sistema, con un impatto sostanziale sulle prestazioni del dispositivo e sull'esperienza utente. Per evitare che ciò accada, preferisci utilizzare la registrazione del contesto anziché la dichiarazione del file manifest. A volte, il sistema Android impone l'uso di ricevitori registrati in base al contesto. Ad esempio, la trasmissione
CONNECTIVITY_ACTION
viene inviata solo a destinatari registrati in base al contesto.Non trasmettere informazioni sensibili utilizzando un intent implicito. Le informazioni possono essere lette da qualsiasi app che si registri per ricevere la trasmissione. Esistono tre modi per controllare chi può ricevere gli annunci:
- Puoi specificare un'autorizzazione quando invii una trasmissione.
- 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. 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 da 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 sul thread principale, dovrebbe essere eseguito e restituito rapidamente. Se devi eseguire operazioni a lunga esecuzione, fai attenzione alla generazione di thread o all'avvio di servizi in background, poiché il sistema può terminare l'intero processo dopo il ritorno dionReceive()
. Per saperne di più, consulta Effetto sullo stato del processo. Per eseguire operazioni a lunga esecuzione, ti consigliamo di:- Chiamata a
goAsync()
nel metodoonReceive()
del ricevitore 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 (inferiore a 10 secondi). Ti permette di spostare il lavoro su un altro thread per evitare di causare problemi al thread principale. - Pianificazione di un lavoro con
JobScheduler
. Per ulteriori informazioni, consulta Pianificazione intelligente dei job.
- Chiamata a
Non avviare attività da ricevitori perché l'esperienza utente è scioccante, specialmente se sono presenti più ricevitori. Potresti, invece, visualizzare una notifica.