Présentation des diffusions

Les applications Android envoient et reçoivent des messages de diffusion du système Android et d'autres applications Android, de la même manière que le modèle de conception publication/abonnement. Le système et les applications envoient généralement des diffusions lorsque certains événements se produisent. Par exemple, le système Android envoie des diffusions lorsque divers événements système se produisent, tels que le démarrage du système ou la recharge de l'appareil. Les applications envoient également des diffusions personnalisées, par exemple pour avertir d'autres applications d'un événement susceptible de les intéresser (par exemple, le téléchargement de nouvelles données).

Les applications peuvent s'inscrire pour recevoir des diffusions spécifiques. Lorsqu'une diffusion est envoyée, le système la redirige automatiquement vers les applications qui se sont abonnées pour recevoir ce type de diffusion spécifique.

En règle générale, les diffusions peuvent être utilisées comme système de messagerie dans les applications et en dehors du parcours utilisateur normal. Toutefois, vous devez veiller à ne pas abuser de la possibilité de répondre aux diffusions et d'exécuter des jobs en arrière-plan qui peuvent contribuer à ralentir les performances du système.

À propos des annonces système

Le système envoie automatiquement des diffusions lorsque divers événements système se produisent, par exemple lorsque le système active ou désactive le mode Avion. Toutes les applications abonnées reçoivent ces diffusions.

L'objet Intent encapsule le message de diffusion. La chaîne action identifie l'événement qui s'est produit, par exemple android.intent.action.AIRPLANE_MODE. L'intention peut également inclure des informations supplémentaires regroupées dans son champ "extra". Par exemple, l'intent "Mode Avion" inclut un extra booléen qui indique si le mode Avion est activé ou non.

Pour en savoir plus sur la lecture des intents et l'obtention de la chaîne d'action à partir d'un intent, consultez Intents et filtres d'intent.

Actions d'annonce du système

Pour obtenir la liste complète des actions d'annonce du système, consultez le fichier BROADCAST_ACTIONS.TXT dans le SDK Android. Chaque action de diffusion est associée à un champ constant. Par exemple, la valeur de la constante ACTION_AIRPLANE_MODE_CHANGED est android.intent.action.AIRPLANE_MODE. La documentation de chaque action de diffusion est disponible dans le champ de constante associé.

Modifications apportées aux diffusions système

À mesure que la plate-forme Android évolue, le comportement des diffusions système change périodiquement. Tenez compte des modifications suivantes pour prendre en charge toutes les versions d'Android.

Android 16

Dans Android 16, l'ordre de diffusion à l'aide de l'attribut android:priority ou IntentFilter.setPriority() dans différents processus ne sera pas garanti. Les priorités de diffusion ne sont respectées que dans le même processus d'application, et non pas dans tous les processus.

De plus, les priorités de diffusion sont automatiquement limitées à la plage (SYSTEM_LOW_PRIORITY + 1, SYSTEM_HIGH_PRIORITY - 1). Seuls les composants système sont autorisés à définir SYSTEM_LOW_PRIORITY, SYSTEM_HIGH_PRIORITY comme priorité de diffusion.

Android 14

Lorsque les applications sont à l'état mis en cache, le système optimise la diffusion pour la santé du système. Par exemple, le système diffère les diffusions système moins importantes telles que ACTION_SCREEN_ON lorsque l'application est dans un état mis en cache. Une fois que l'application passe de l'état mis en cache à un cycle de vie de processus actif, le système diffuse toutes les diffusions différées.

Les diffusions importantes déclarées dans le fichier manifeste suppriment temporairement les applications de l'état mis en cache pour la diffusion.

Android 9

À partir d'Android 9 (niveau d'API 28), la diffusion NETWORK_STATE_CHANGED_ACTION ne reçoit pas d'informations sur la position de l'utilisateur ni de données permettant de l'identifier personnellement.

Si votre application est installée sur un appareil équipé d'Android 9.0 (niveau d'API 28) ou version ultérieure, le système n'inclut pas les SSID, les BSSID, les informations de connexion ni les résultats d'analyse dans les diffusions Wi-Fi. Pour obtenir ces informations, appelez plutôt getConnectionInfo().

Android 8.0

À partir d'Android 8.0 (niveau d'API 26), le système impose des restrictions supplémentaires aux récepteurs déclarés dans le fichier manifeste.

Si votre application cible Android 8.0 ou version ultérieure, vous ne pouvez pas utiliser le fichier manifeste pour déclarer un récepteur pour la plupart des diffusions implicites (diffusions qui ne ciblent pas spécifiquement votre application). Vous pouvez toujours utiliser un récepteur enregistré dans le contexte lorsque l'utilisateur utilise activement votre application.

Android 7.0

Android 7.0 (niveau d'API 24) et les versions ultérieures n'envoient pas les diffusions système suivantes :

De plus, les applications ciblant Android 7.0 et les versions ultérieures doivent enregistrer la diffusion CONNECTIVITY_ACTION à l'aide de registerReceiver(BroadcastReceiver, IntentFilter). La déclaration d'un récepteur dans le fichier manifeste ne fonctionne pas.

Recevoir des diffusions

Les applications peuvent recevoir des diffusions de deux manières : via des récepteurs enregistrés dans le contexte et des récepteurs déclarés dans le fichier manifeste.

Récepteurs enregistrés dans le contexte

Les récepteurs enregistrés en contexte reçoivent les diffusions tant que leur contexte d'enregistrement est valide. Il se situe généralement entre les appels à registerReceiver et unregisterReceiver. Le contexte d'enregistrement devient également non valide lorsque le système détruit le contexte correspondant. Par exemple, si vous vous inscrivez dans un contexte Activity, vous recevez des diffusions tant que l'activité reste active. Si vous vous enregistrez avec le contexte d'application, vous recevez des diffusions tant que l'application est en cours d'exécution.

Pour enregistrer un récepteur avec un contexte, procédez comme suit :

  1. Dans le fichier de compilation au niveau du module de votre application, incluez la version 1.9.0 ou ultérieure de la bibliothèque AndroidX Core :

    Groovy

    dependencies {
        def core_version = "1.19.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.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.19.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.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")
    }
  2. Créez une instance de BroadcastReceiver :

    Kotlin

    val myBroadcastReceiver = MyBroadcastReceiver()
    

    Java

    MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
    
  3. Créez une instance de IntentFilter :

    Kotlin

    val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")
    

    Java

    IntentFilter filter = new IntentFilter("com.example.snippets.ACTION_UPDATE_DATA");
    
  4. Indiquez si le récepteur de diffusion doit être exporté et visible par d'autres applications sur l'appareil. Si ce récepteur écoute les diffusions envoyées par le système ou par d'autres applications (même celles qui vous appartiennent), utilisez l'indicateur RECEIVER_EXPORTED. Si ce récepteur n'écoute que les diffusions envoyées par votre application, utilisez le flag RECEIVER_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;
    
  5. Enregistrez le récepteur en appelant registerReceiver() :

    Kotlin

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

    Java

    ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags);
    
  6. Pour ne plus recevoir de diffusions, appelez unregisterReceiver(android.content.BroadcastReceiver). Veillez à désinscrire le récepteur lorsque vous n'en avez plus besoin ou que le contexte n'est plus valide.

Désenregistrer votre broadcast receiver

Tant que le broadcast receiver est enregistré, il conserve une référence au contexte avec lequel vous l'avez enregistré. Cela peut potentiellement entraîner des fuites si le champ d'application enregistré du récepteur dépasse le champ d'application du cycle de vie du contexte. Par exemple, cela peut se produire lorsque vous enregistrez un récepteur dans une portée d'activité, mais que vous oubliez de le désenregistrer lorsque le système détruit l'activité. Par conséquent, désenregistrez toujours votre 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
    }
}

Enregistrer les récepteurs dans le champ d'application le plus petit

Votre broadcast receiver ne doit être enregistré que lorsque vous êtes réellement intéressé par le résultat. Choisissez le champ d'application du récepteur le plus petit possible :

  • Méthodes de cycle de vie LifecycleResumeEffect ou d'activité onResume/onPause : le récepteur de diffusion ne reçoit les mises à jour que lorsque l'application est à l'état "Reprise".
  • Méthodes de cycle de vie LifecycleStartEffect ou d'activité onStart/onStop : le récepteur de diffusion ne reçoit les mises à jour que lorsque l'application est à l'état "Reprise".
  • DisposableEffect : le broadcast receiver ne reçoit les mises à jour que lorsque le composable se trouve dans l'arborescence de composition. Cette portée n'est pas associée à la portée du cycle de vie de l'activité. Envisagez d'enregistrer le récepteur dans le contexte de l'application. En effet, le composable pourrait théoriquement survivre au champ d'application du cycle de vie de l'activité et entraîner une fuite de l'activité.
  • Activité onCreate/onDestroy : le récepteur de diffusion reçoit les mises à jour lorsque l'activité est dans son état créé. Veillez à annuler l'enregistrement dans onDestroy() et non dans onSaveInstanceState(Bundle), car cette méthode peut ne pas être appelée.
  • Portée personnalisée : par exemple, vous pouvez enregistrer un récepteur dans votre portée ViewModel afin qu'il survive à la recréation de l'activité. Veillez à utiliser le contexte de l'application pour enregistrer le récepteur, car le récepteur peut survivre à la portée du cycle de vie de l'activité et fuir l'activité.

Créer des composables avec et sans état

Compose comporte des composables avec et sans état. L'enregistrement ou la désinscription d'un broadcast receiver dans un composable le rend avec état. Le composable n'est pas une fonction déterministe qui affiche le même contenu lorsque les mêmes paramètres lui sont transmis. L'état interne peut changer en fonction des appels au récepteur de diffusion enregistré.

Dans Compose, nous vous recommandons de diviser vos composables en versions avec et sans état. Par conséquent, nous vous recommandons de hisser la création du broadcast receiver hors d'un composable pour le rendre sans état :

@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
}

Récepteurs déclarés dans le fichier manifeste

Si vous déclarez un récepteur de diffusion dans votre fichier manifeste, le système lance votre application lorsque la diffusion est envoyée. Si l'application n'est pas déjà en cours d'exécution, le système la lance.

Pour déclarer un récepteur de diffusion dans le fichier manifeste, procédez comme suit :

  1. Spécifiez l'élément <receiver> dans le fichier manifeste de votre application.

    <!-- 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>
    

    Les filtres d'intent spécifient les actions de diffusion auxquelles votre récepteur est abonné.

  2. Sous-classez BroadcastReceiver et implémentez onReceive(Context, Intent). Le broadcast receiver de l'exemple suivant enregistre et affiche le contenu de la diffusion :

    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); }
            }
        }
    }
    

Le gestionnaire de packages système enregistre le récepteur lorsque l'application est installée. Le récepteur devient alors un point d'entrée distinct dans votre application, ce qui signifie que le système peut démarrer l'application et diffuser la diffusion si l'application n'est pas en cours d'exécution.

Le système crée un objet de composant BroadcastReceiver pour gérer chaque diffusion qu'il reçoit. Cet objet n'est valide que pendant la durée de l'appel à onReceive(Context, Intent). Une fois que votre code est renvoyé par cette méthode, le système considère que le composant n'est plus actif.

Effets sur l'état du processus

Le fait que votre BroadcastReceiver fonctionne ou non affecte le processus qu'il contient, ce qui peut modifier sa probabilité de provoquer un arrêt du système. Un processus de premier plan exécute la méthode onReceive() d'un récepteur. Le système exécute le processus, sauf en cas de pression extrême sur la mémoire.

Le système désactive BroadcastReceiver après onReceive(). L'importance du processus hôte du récepteur dépend de ses composants d'application. Si ce processus n'héberge qu'un récepteur déclaré dans le fichier manifeste, le système peut l'arrêter après onReceive() pour libérer des ressources pour d'autres processus plus critiques. Cela se produit généralement pour les applications avec lesquelles l'utilisateur n'a jamais interagi ou n'a pas interagi récemment.

Par conséquent, les broadcast receivers ne doivent pas lancer de threads en arrière-plan de longue durée. Le système peut arrêter le processus à tout moment après onReceive() pour récupérer de la mémoire, en mettant fin au thread créé. Pour que le processus reste actif, planifiez un JobService à partir du récepteur à l'aide de JobScheduler afin que le système sache que le processus est toujours en cours d'exécution. Pour en savoir plus, consultez Présentation des tâches en arrière-plan.

Envoyer des diffusions

Android propose aux applications deux méthodes pour envoyer des diffusions :

  • La méthode sendOrderedBroadcast(Intent, String) envoie les diffusions à un récepteur à la fois. Chaque destinataire peut propager un résultat au destinataire suivant. Il peut également interrompre complètement la diffusion afin qu'elle n'atteigne pas d'autres récepteurs. Vous pouvez contrôler l'ordre d'exécution des récepteurs dans le même processus d'application. Pour ce faire, utilisez l'attribut android:priority du filtre d'intent correspondant. Les récepteurs ayant la même priorité sont exécutés dans un ordre arbitraire.
  • La méthode sendBroadcast(Intent) envoie des diffusions à tous les récepteurs dans un ordre indéfini. C'est ce qu'on appelle une diffusion normale. Cette méthode est plus efficace, mais signifie que les récepteurs ne peuvent pas lire les résultats d'autres récepteurs, propager les données reçues de la diffusion ou annuler la diffusion.

L'extrait de code suivant montre comment envoyer une diffusion en créant un Intent et en appelant 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);

L'annonce est encapsulée dans un objet Intent. La chaîne action de l'intent doit fournir la syntaxe du nom de package Java de l'application et identifier de manière unique l'événement de diffusion. Vous pouvez joindre des informations supplémentaires à l'intention avec putExtra(String, Bundle). Vous pouvez également limiter une diffusion à un ensemble d'applications de la même organisation en appelant setPackage(String) sur l'intention.

Restreindre les diffusions avec des autorisations

Les autorisations vous permettent de limiter les diffusions à l'ensemble des applications qui détiennent certaines autorisations. Vous pouvez appliquer des restrictions à l'expéditeur ou au destinataire d'une diffusion.

Envoyer des diffusions avec des autorisations

Lorsque vous appelez sendBroadcast(Intent, String) ou sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle), vous pouvez spécifier un paramètre d'autorisation. Seuls les récepteurs ayant demandé cette autorisation avec la balise <uses-permission> dans leur fichier manifeste peuvent recevoir la diffusion. Si l'autorisation est dangereuse, vous devez l'accorder avant que le récepteur puisse recevoir la diffusion. Par exemple, le code suivant envoie une diffusion avec une autorisation :

Kotlin

context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION)

Java

context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION);

Pour recevoir la diffusion, l'application de réception doit demander l'autorisation comme suit :

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

Vous pouvez spécifier une autorisation système existante, comme BLUETOOTH_CONNECT, ou définir une autorisation personnalisée avec l'élément <permission>. Pour en savoir plus sur les autorisations et la sécurité en général, consultez Autorisations système.

Recevoir des diffusions avec des autorisations

Si vous spécifiez un paramètre d'autorisation lors de l'enregistrement d'un broadcast receiver (avec registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) ou dans la balise <receiver> de votre fichier manifeste), seuls les diffuseurs ayant demandé l'autorisation avec la balise <uses-permission> dans leur fichier manifeste peuvent envoyer un intent au broadcast receiver. Si l'autorisation est dangereuse, elle doit également être accordée au diffuseur.

Par exemple, supposons que votre application de réception dispose d'un récepteur déclaré dans le fichier manifeste comme suit :

<!-- 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 votre application de réception dispose d'un récepteur enregistré en contexte comme suit :

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
);

Ensuite, pour pouvoir envoyer des diffusions à ces récepteurs, l'application émettrice doit demander l'autorisation comme suit :

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

Éviter les diffusions dans le même processus

Les diffusions sont conçues comme un mécanisme de communication inter-processus (IPC) permettant d'envoyer des messages entre différentes applications ou entre le système et les applications. L'envoi d'une diffusion automatique, c'est-à-dire une diffusion pour laquelle tous les récepteurs s'exécutent dans le même processus que celui qui a envoyé la diffusion, est très inefficace, crée des surcoûts système inutiles et est fortement déconseillé.

Voici deux scénarios courants dans lesquels les applications s'envoient des diffusions à elles-mêmes :

  • Communiquer entre les composants d'un même processus : par exemple, transmettre des événements ou des données entre des activités, des fragments, des services ou des threads d'arrière-plan. Au lieu d'envoyer des diffusions, utilisez des mécanismes de communication standards dans le processus, tels que le modèle d'observateur ou les flux réactifs :

    • Flux Kotlin (SharedFlow et StateFlow) : solution moderne et idiomatique en Kotlin pour émettre et observer des flux d'événements ou des mises à jour d'état dans les coroutines et les composants de votre application.
    • ViewModel partagé : facilite le partage de données et d'événements entre différents composants d'UI (tels que des fragments ou des composables) au sein d'une même activité.
    • Rappels et écouteurs : rappels d'interface standard ou références de fonction transmises directement entre les composants ou enregistrées auprès d'un dépôt ou d'un contrôleur central.
  • Gérer les événements, les tâches ou les alarmes système : par exemple, recevoir une tâche planifiée, une alarme ou un rappel système, puis envoyer une diffusion pour déclencher le travail proprement dit. Au lieu d'envoyer une diffusion, effectuez le travail directement dans ce job (par exemple, un worker JobService ou WorkManager), un gestionnaire d'alarme ou un composant de rappel système, ou déléguez directement aux classes de logique métier de votre application.

Sur les appareils équipés d'Android 17 QPR2 ou version ultérieure, le système distribue les diffusions automatiques plus efficacement en les renvoyant au processus d'envoi, qui les distribue à ses propres récepteurs sur le thread principal. L'envoi d'une diffusion à soi-même n'augmente pas l'importance de votre processus et n'empêche pas la mise en veille d'un processus mis en cache. N'utilisez donc pas les diffusions à soi-même pour maintenir votre application en cours d'exécution ou pour effectuer des tâches en arrière-plan. Même avec cette optimisation, les diffusions à soi-même sont moins efficaces que les mécanismes de communication en cours de traitement. Utilisez donc ces alternatives à la place.

Pour en savoir plus sur la conception de la communication entre les composants d'application, consultez le guide de l'architecture des applications.

Points à noter concernant la sécurité

Voici quelques éléments de sécurité à prendre en compte pour l'envoi et la réception de diffusions :

  • Si de nombreuses applications sont enregistrées pour recevoir la même diffusion dans leur fichier manifeste, le système peut lancer de nombreuses applications, ce qui a un impact important sur les performances de l'appareil et sur l'expérience utilisateur. Pour éviter cela, préférez l'enregistrement du contexte à la déclaration du fichier manifeste. Parfois, le système Android lui-même impose l'utilisation de récepteurs enregistrés dans le contexte. Par exemple, la diffusion CONNECTIVITY_ACTION n'est distribuée qu'aux récepteurs enregistrés dans le contexte.

  • Ne diffusez pas d'informations sensibles à l'aide d'un intent implicite. N'importe quelle application peut lire les informations si elle s'inscrit pour recevoir la diffusion. Vous pouvez contrôler qui peut recevoir vos diffusions de trois manières :

    • Vous pouvez spécifier une autorisation lorsque vous envoyez une diffusion.
    • Dans Android 4.0 (niveau d'API 14) et versions ultérieures, vous pouvez spécifier un package avec setPackage(String) lorsque vous envoyez une diffusion. Le système limite la diffusion à l'ensemble des applications correspondant au package.
  • Lorsque vous enregistrez un récepteur, n'importe quelle application peut envoyer des diffusions potentiellement malveillantes au récepteur de votre application. Il existe plusieurs façons de limiter les diffusions que votre application reçoit :

    • Vous pouvez spécifier une autorisation lorsque vous enregistrez un broadcast receiver.
    • Pour les récepteurs déclarés dans le fichier manifeste, vous pouvez définir l'attribut android:exported sur "false" dans le fichier manifeste. Le récepteur ne reçoit pas les diffusions provenant de sources externes à l'application.
  • L'espace de noms des actions de diffusion est global. Assurez-vous que les noms d'action et les autres chaînes sont écrits dans un espace de noms qui vous appartient. Sinon, vous risquez d'entrer en conflit avec d'autres applications par inadvertance.

  • Étant donné que la méthode onReceive(Context, Intent) d'un récepteur s'exécute sur le thread principal, elle doit s'exécuter et renvoyer rapidement. Si vous devez effectuer un travail de longue durée, faites attention à la création de threads ou au démarrage de services en arrière-plan, car le système peut arrêter l'ensemble du processus après le retour de onReceive(). Pour en savoir plus, consultez Effet sur l'état du processus. Pour effectuer des tâches de longue durée, nous vous recommandons :

    • Appelez goAsync() dans la méthode onReceive() de votre récepteur et transmettez BroadcastReceiver.PendingResult à un thread en arrière-plan. Cela permet de maintenir la diffusion active après le retour de onReceive(). Toutefois, même avec cette approche, le système s'attend à ce que vous terminiez la diffusion très rapidement (en moins de 10 secondes). Il vous permet de déplacer des tâches vers un autre thread pour éviter de perturber le thread principal.
    • Planifier un job avec JobScheduler. Pour en savoir plus, consultez Planification intelligente des jobs.
  • Ne lancez pas d'activités à partir de broadcast receivers, car l'expérience utilisateur est désagréable, surtout s'il y a plusieurs récepteurs. Envisagez plutôt d'afficher une notification.