Panoramica delle trasmissioni

Le app per Android possono inviare o ricevere annunci dal sistema Android e altre app per Android, simili pubblicare-sottoscrivere pattern di progettazione. Queste trasmissioni vengono inviate quando si verifica un evento di interesse. Ad esempio, il sistema Android invia annunci quando vari eventi di sistema quando il sistema si avvia o il dispositivo inizia a ricaricarsi. App Possono anche inviare annunci personalizzati, ad esempio per informare altre app di qualcosa a cui potrebbero essere interessati (ad esempio, alcuni nuovi dati sono scaricati).

Il sistema ottimizza la distribuzione delle trasmissioni per mantenere integrità ottimale del sistema. Pertanto, i tempi di consegna delle trasmissioni garantito. Le app che richiedono una comunicazione tra processi a bassa latenza prendi in considerazione i servizi associati.

Le app possono registrarsi per ricevere trasmissioni specifiche. Quando viene inviata una trasmissione, il sistema instrada automaticamente le trasmissioni alle app a cui è abbonato ricevere quel particolare tipo di trasmissione.

In generale, le trasmissioni possono essere utilizzate come sistema di messaggistica tra app. e al di fuori del normale flusso utente. Bisogna però fare attenzione a non abusare l'opportunità di rispondere alle trasmissioni ed eseguire job in background che può contribuire a rallentare le prestazioni del sistema.

Informazioni sulle trasmissioni di sistema

Il sistema invia automaticamente gli annunci quando si verificano vari eventi di sistema, ad esempio quando il sistema attiva e disattiva la modalità aereo. Sistema vengono inviati a tutte le app iscritte per ricevere i .

Il messaggio broadcast stesso è aggregato in Intent la cui stringa di azione identifica l'evento che si è verificato (ad esempio android.intent.action.AIRPLANE_MODE). L'intento può anche includere informazioni aggiuntive raggruppate nel relativo campo aggiuntivo. Ad esempio, l'aereo l'intent mode include un extra booleano che indica se la modalità Aereo La modalità è attiva.

Per ulteriori informazioni su come leggere gli intent e ottenere la stringa di azione da per intenti, consulta Intent Filtri.

Per un elenco completo delle azioni di trasmissione del sistema, consulta BROADCAST_ACTIONS.TXT nell'SDK Android. Ogni azione di trasmissione ha un campo costante associato. Ad esempio, il valore della costante ACTION_AIRPLANE_MODE_CHANGED è android.intent.action.AIRPLANE_MODE. Documentazione per ogni azione di trasmissione è disponibile nel relativo campo della costante associata.

Modifiche alle trasmissioni di sistema

Con l'evoluzione della piattaforma Android, cambia periodicamente il modo in cui il sistema trasmette comportarsi. Tieni presente le modifiche che seguono per supportare tutte le versioni di Android.

Android 14

Mentre le app sono memorizzate nella cache , la distribuzione della trasmissione ottimizzate per l'integrità del sistema. Ad esempio, gli annunci di sistema meno importanti come ACTION_SCREEN_ON sono differita quando l'app è in stato di cache. Una volta che l'app passa dalla cache in un processo attivo ciclo di vita, il sistema offre eventuali trasmissioni differite.

Le trasmissioni importanti che sono dichiarate nel nel file manifest, di rimuovere temporaneamente le app per il caricamento.

Android 9

A partire da Android 9 (livello API 28), la funzionalità NETWORK_STATE_CHANGED_ACTION la trasmissione non riceve informazioni sulla posizione o sull'identità personale dell'utente identificabili.

Inoltre, se la tua app è installata su un dispositivo con Android 9 o versioni successive: le trasmissioni di sistema da Wi-Fi non contengono SSID, BSSID, connessioni informazioni o i risultati della scansione. Per ottenere queste informazioni, chiama getConnectionInfo() .

Android 8.0

A partire da Android 8.0 (livello API 26), il sistema impone restrizioni sui destinatari dichiarati da 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 (le trasmissioni che non hanno come target specifica la tua app). Puoi comunque utilizzare un ricevitore registrato al contesto quando che l'utente sta utilizzando attivamente la tua app.

Android 7.0

Android 7.0 (livello API 24) e versioni successive non inviano il seguente sistema annunci:

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 file manifest non funziona.

Ricezione di annunci

Le app possono ricevere annunci in due modi: tramite ricevitori dichiarati da un manifest e i destinatari registrati al contesto.

Destinatari dichiarati nei file manifest

Se dichiari un broadcast receiver nel tuo manifest, il sistema avvia il tuo (se non è già in esecuzione) quando viene inviata la trasmissione.

Per dichiarare un broadcast receiver nel manifest, segui questi passaggi:

  1. Specifica il campo <receiver> nel file 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 si registra il destinatario.

  2. Sottoclasse BroadcastReceiver e implementa onReceive(Context, Intent). La nell'esempio che segue registra il broadcast receiver e ne mostra 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 attivare l'associazione delle viste, configurare viewBinding a livello di modulo il file build.gradle.

Il gestore di pacchetti di sistema registra il ricevitore quando l'app è installata. Il destinatario diventa quindi un punto di accesso separato nella tua app, il che significa in modo che il sistema possa avviare l'app e trasmettere il broadcast se l'app non attualmente in esecuzione.

Il sistema crea un nuovo componente BroadcastReceiver per gestire ogni trasmissione ricevuta. Questo oggetto è valido solo per l'intera durata della chiamata a onReceive(Context, Intent). Una volta che il codice da questo metodo, il sistema considera il componente non più attivo.

Destinatari registrati al contesto

I ricevitori registrati al contesto ricevono le trasmissioni finché la loro registrazione il contesto sia valido. Ad esempio, se ti registri all'interno di un Activity contesto, si ricevono trasmissioni purché l'attività non venga distrutta. Se registrati con il contesto dell'applicazione, ricevi trasmissioni finché l'app in esecuzione.

Per registrare un destinatario con un contesto, segui questi passaggi:

  1. Nel file di build a livello di modulo dell'app, includi la versione 1.9.0 o una versione successiva di la libreria AndroidX Core:

    Groovy

    dependencies {
        def core_version = "1.15.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"
        // 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-alpha02"
    }
    

    Kotlin

    dependencies {
        val core_version = "1.15.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")
        // 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-alpha02")
    }
    
  2. Crea un'istanza di BroadcastReceiver:

    Kotlin

    val br: BroadcastReceiver = MyBroadcastReceiver()
    

    Java

    BroadcastReceiver br = new MyBroadcastReceiver();
    
  3. Crea un'istanza di IntentFilter:

    Kotlin

    val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
    

    Java

    IntentFilter filter = new IntentFilter(APP_SPECIFIC_BROADCAST);
    
  4. Scegli se il broadcast receiver deve essere esportato e visibile a altre app sul dispositivo. Se questo ricevitore sta ascoltando gli annunci inviati dal sistema o da altre app, anche da altre app di tua proprietà, usa RECEIVER_EXPORTED flag. Se invece questo ricevitore ascolta solo annunci inviati dalla tua app, usa il 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. Registra il destinatario chiamando registerReceiver():

    Kotlin

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

    Java

    ContextCompat.registerReceiver(context, br, filter, receiverFlags);
    
  6. Per interrompere la ricezione degli annunci, chiama unregisterReceiver(android.content.BroadcastReceiver). Assicurati di annullare la registrazione del destinatario quando non ti serve più o il contesto non è più valido.

    Fai attenzione a dove ti registri e annulli la registrazione del destinatario, Ad esempio, se registri un destinatario in onCreate(Bundle) utilizzando il contesto dell'attività, dovrebbe annullare la sua registrazione in onDestroy() per evitare che il destinatario venga divulgato fuori dal contesto dell'attività. Se ti registri ricevitore in onResume(), annulla la sua registrazione in onPause() per evitare registrandolo più volte (se non vuoi ricevere annunci quando sono in pausa, per ridurre l'overhead superfluo del sistema). Azioni sconsigliate annulla registrazione in onSaveInstanceState(Bundle), perché non viene chiamato se l'utente torna indietro nell'elenco della cronologia.

Effetti sullo stato del processo

Se BroadcastReceiver sta funzionando o non influisce sul processo contenuto, il che può alterare i suoi probabilità di uccidere i sistemi. Un processo in primo piano esegue il metodo onReceive() di un destinatario. La di sistema esegue il processo, tranne in caso di estrema pressione di memoria.

Il BroadcastRicevir viene disattivato dopo il giorno onReceive(). L'host del destinatario è importante solo quanto i componenti dell'app. Se questo processo ospita solo un ricevitore dichiarato da manifest (si verifica frequentemente per le app che l'utente non ha mai o non ha interagito di recente), il sistema potrebbe terminarlo dopo onReceive() per e le risorse disponibili per altri processi più critici.

Di conseguenza, i broadcast receiver non dovrebbero avviare thread in background a lunga esecuzione. Il sistema può interrompere la procedura in qualsiasi momento dopo il giorno onReceive() per la rivendicazione memoria, terminando il thread creato. Per mantenere attivo il processo, pianifica una JobService dal destinatario utilizzando il JobScheduler in modo che il sistema sappia che la procedura è ancora in corso. Panoramica del lavoro in background fornisce ulteriori dettagli.

Invio di annunci

Android offre alle app tre modi per inviare annunci:

  • sendOrderedBroadcast(Intent, String) invia annunci a un ricevitore alla volta. Quando ogni ricevitore esegue a sua volta, può propagare un risultato al ricevitore successivo oppure interrompere completamente la trasmissione in modo che non venga passata ad altri o ricevitori. L'ordine di esecuzione dei ricevitori può essere controllato con l'attributo android:Priority del filtro per intent corrispondente; con il la stessa priorità verrà eseguita in un ordine arbitrario.
  • Il metodo sendBroadcast(Intent) invia trasmette a tutti i ricevitori in un ordine indefinito. Questo fenomeno è chiamato Trasmetti. Questa modalità è più efficiente, ma significa che i destinatari non possono leggere i risultati da altri ricevitori, propagare i dati ricevuti dalla trasmissione interrompere la trasmissione.

Il seguente snippet di codice mostra come inviare un annuncio creando un Intent e chiamata a 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 a 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) per l'intent.

Limitazione delle trasmissioni con autorizzazioni

Le autorizzazioni ti consentono di limitare le trasmissioni all'insieme di app che contengono alcune autorizzazioni. Puoi applicare restrizioni al mittente o ricevente di un annuncio.

Invio con autorizzazioni

Quando chiami sendBroadcast(Intent, String) o sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle), puoi specificare parametro di autorizzazione. Solo i destinatari che hanno richiesto l'autorizzazione con il tag nel file manifest (e, successivamente, gli è stato concesso autorizzazione se è pericoloso) possono ricevere la trasmissione. Ad esempio, il seguente codice invia un broadcast:

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 in quanto come mostrato di seguito:

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

Puoi specificare un'autorizzazione di sistema esistente, come BLUETOOTH_CONNECT oppure definisci un'autorizzazione personalizzata Elemento <permission>. Per informazioni sulle autorizzazioni e sulla sicurezza in generale, consulta le Informazioni sul Autorizzazioni.

Ricezione con autorizzazioni

Se specifichi un parametro di autorizzazione durante la registrazione di un broadcast receiver (con registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) o in Tag <receiver> in ), solo gli emittenti che hanno richiesto l'autorizzazione con il Tag <uses-permission> nel proprio file manifest (e successivamente riceve l'autorizzazione, se pericoloso) possono inviare un intent al destinatario.

Ad esempio, supponiamo che la tua app ricevente abbia un destinatario dichiarato nel file manifest come 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>

In alternativa, 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 richiedi l'autorizzazione come illustrato di seguito:

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

Considerazioni sulla sicurezza e best practice

Di seguito sono riportate alcune considerazioni sulla sicurezza e best practice per l'invio e la ricezione di trasmissioni:

  • Se molte app si sono registrate per ricevere lo stesso annuncio manifest, può causare l'avvio di molte app da parte del sistema, causando un un impatto significativo sia sulle prestazioni del dispositivo sia sull'esperienza utente. Da evitare In questo caso, è preferibile utilizzare la registrazione del contesto piuttosto che la dichiarazione del file manifest. A volte, il sistema Android stesso impone l'uso di o ricevitori. Ad esempio, la trasmissione CONNECTIVITY_ACTION viene consegnata solo a destinatari registrati a contesto.

  • Non trasmettere informazioni sensibili utilizzando un intent implicito. La 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 per l'invio di una trasmissione.
    • In Android 4.0 e versioni successive, puoi specificare package con setPackage(String) durante l'invio di un la trasmissione. Il sistema limita la trasmissione all'insieme di app che corrispondono al pacchetto.
  • Quando registri un destinatario, qualsiasi app può inviare messaggi potenzialmente dannosi gli annunci al ricevitore della tua app. Esistono diversi modi per limitare il annunci ricevuti dalla tua app:

    • Puoi specificare un'autorizzazione quando registri un broadcast receiver.
    • Per i destinatari dichiarati da un file manifest, puoi impostare android:esportato attributo su "false" nel file manifest. Il destinatario non riceve da fonti esterne all'app.
  • Lo spazio dei nomi per le azioni di trasmissione è globale. Assicurati che i nomi delle azioni e altre stringhe sono scritte in uno spazio dei nomi di tua proprietà, oppure entrare inavvertitamente in conflitto con altre app.

  • Perché il metodo onReceive(Context, Intent) di un destinatario viene eseguito nel thread principale, dovrebbe essere eseguito e restituito rapidamente. Per eseguire lavori a lunga durata, fare attenzione a generare fili o avviare in background perché il sistema può terminare l'intero processo Resi a onReceive(). Per ulteriori informazioni, consulta la sezione Effetto sul processo Per eseguire lavori a lunga durata, consiglia:

    • Chiamata a goAsync() nel tuo metodo onReceive() del destinatario e passare BroadcastReceiver.PendingResult a un thread in background. Questa operazione mantiene attiva la trasmissione dopo il ritorno da onReceive(). Tuttavia, anche con questo approccio il sistema si aspetta che tu finisca con molto rapidamente (meno di 10 secondi). Ti permette invece di spostare lavora su un altro thread per evitare di creare problemi con il thread principale.
    • Pianificazione di un job con JobScheduler. Per ulteriori informazioni informazioni, vedi Job intelligente Programmazione.
  • Non avviare attività da broadcast receiver perché l'esperienza utente è irritante; soprattutto se c'è più di un ricevitore. Prendi in considerazione invece mostrare una notifica.