DisposableEffect — libération de ressources dans Jetpack Compose

Auteur : IT Sectr Publié le : 2026-06-30 Temps de lecture : 9 min

DisposableEffect est une fonction composable dans Jetpack Compose conçue pour les opérations nécessitant une initialisation explicite et un nettoyage ultérieur des ressources. Contrairement aux autres API d’effets secondaires, DisposableEffect fournit un bloc onDispose qui est garanti de s’exécuter lorsque le composant quitte la composition ou lorsque la clé change. Cela le rend indispensable pour travailler avec des abonnements natifs, des écouteurs de capteurs et des ressources matérielles. Selon Android Developers Documentation (2025), DisposableEffect est recommandé dans tous les scénarios nécessitant une paire setup/teardown, analogue à onStart/onStop dans le cycle de vie d’Activity.

Points clés

  • DisposableEffect — API d’effet secondaire pour la configuration et le nettoyage garanti des ressources.
  • onDispose — bloc obligatoire exécuté lors de la sortie de la composition ou du changement de clé.
  • Synchrone — contrairement à LaunchedEffect, DisposableEffect s’exécute de manière synchrone sans coroutines.
  • Nettoyage — scénarios typiques : se désabonner de LiveData, fermer des sockets, annuler l’enregistrement de BroadcastReceiver.
  • Clés — lors du changement de clé, onDispose est exécuté pour l’ancienne valeur et une réinitialisation se produit avec la nouvelle.

Qu’est-ce que DisposableEffect dans Jetpack Compose

DisposableEffect est un outil clé pour la gestion des ressources dans Jetpack Compose. Sa principale caractéristique est l’invocation garantie du bloc onDispose lorsque le cycle de vie du composant composable se termine. Ce comportement est crucial pour le développement Android, où les abonnements non fermés aux services système peuvent entraîner des fuites mémoire et des crashs d’application.

Contrairement à LaunchedEffect, qui s’exécute dans un contexte asynchrone de coroutine, DisposableEffect s’exécute de manière synchrone. Cela signifie que vous ne pouvez pas appeler de fonctions suspend à l’intérieur. L’exécution synchrone garantit la prévisibilité : vous pouvez être sûr que le code d’initialisation s’exécute avant le premier rendu et que le code de nettoyage s’exécute avant que le composant ne soit retiré de la mémoire.

Selon la Documentation Jetpack Compose (2025), DisposableEffect doit être utilisé dans quatre scénarios principaux : (1) abonnement aux services système (capteurs, LocationManager), (2) enregistrement de BroadcastReceiver, (3) travail avec des bibliothèques basées sur des callbacks ne supportant pas les coroutines, (4) liaison de composants Compose aux systèmes View legacy via AndroidView.

kotlin
class SensorManager(private val context: Context) {
    fun startListening(callback: (Float) -> Unit) { /* register */ }
    fun stopListening() { /* cancel */ }
}

@Composable
fun SensorDisplay() {
    val sensorManager = remember { SensorManager(context) }
    var value by remember { mutableStateOf(0f) }
    
    DisposableEffect(Unit) {
        sensorManager.startListening { value = it }
        onDispose { sensorManager.stopListening() }
    }
    
    Text("Capteur : $value")
}

Comment DisposableEffect fonctionne avec onDispose

Le mécanisme interne de DisposableEffect est basé sur les phases du cycle de vie de la composition. Lorsqu’un composant composable entre dans la composition, DisposableEffect exécute le bloc de code passé. Ce bloc retourne un objet DisposableEffectResult contenant la lambda onDispose. La composition enregistre ce résultat et appelle onDispose lorsque le composant quitte la composition — quelle que soit la raison (navigation, changement d’état parent, suppression de LazyColumn).

Le mécanisme de clés dans DisposableEffect fonctionne de manière similaire à LaunchedEffect : lorsqu’une clé change, onDispose est d’abord exécuté pour l’ancien état, puis le bloc d’initialisation est relancé avec les nouvelles clés. Cela permet de reconfigurer une ressource lorsque ses paramètres changent. Par exemple, si la clé est une URL de socket, lorsqu’elle change, l’ancien socket est fermé et un nouveau est ouvert.

Important : le bloc onDispose est un élément obligatoire de DisposableEffect. Si vous n’appelez pas onDispose à l’intérieur du bloc, le code ne compilera pas. Cette exigence du compilateur garantit que le développeur n’oublie pas de prévoir le nettoyage des ressources, ce qui est une cause fréquente d’erreurs dans la gestion manuelle des abonnements.

kotlin
// Utilisation correcte avec une clé
DisposableEffect(sensorType) {
    val sensor = sensorManager.getDefaultSensor(sensorType)
    sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL)
    
    onDispose {
        sensorManager.unregisterListener(listener)
    }
}

// Plusieurs ressources dans un seul DisposableEffect
DisposableEffect(Unit) {
    context.registerReceiver(receiver, intentFilter)
    lifecycle.addObserver(observer)
    
    onDispose {
        context.unregisterReceiver(receiver)
        lifecycle.removeObserver(observer)
    }
}

DisposableEffect contre les fuites mémoire

Les fuites mémoire dans les applications Android surviennent souvent à cause d’écouteurs et d’abonnements non désenregistrés qui continuent de conserver une référence à une Activity ou un Context après la fermeture de l’écran. DisposableEffect résout ce problème au niveau du framework : si le développeur utilise DisposableEffect pour enregistrer un écouteur, onDispose garantit d’annuler l’abonnement dans tout scénario de fin de composant.

C’est particulièrement crucial pour LazyColumn et LazyGrid, où les éléments sont constamment créés et détruits au fur et à mesure que l’utilisateur fait défiler. Sans DisposableEffect, chaque élément qui disparaît de la zone visible laisserait un abonnement actif. Avec DisposableEffect, onDispose est appelé pour chaque élément déchargé, garantissant que les ressources sont libérées immédiatement après que l’élément quitte l’écran.

Selon Android Performance Patterns (Google, 2025), l’utilisation de DisposableEffect pour tous les abonnements natifs réduit le nombre de fuites mémoire dans les applications Compose de 60–70% par rapport à la gestion manuelle via des callbacks de cycle de vie. Le système lui-même suit le moment où le composant quitte la composition et garantit l’exécution de onDispose même lors de la fermeture d’urgence de l’écran.

RessourceCe que fait DisposableEffectSans DisposableEffect
BroadcastReceiverregister + onDispose → unregisterLe récepteur reste actif
SensorManagerregisterListener + onDispose → unregisterListenerLe capteur continue d’envoyer des données
Observable (pas Flow)subscribe + onDispose → unsubscribeLe callback conserve une référence
TextureView / SurfaceViewsetCallback + onDispose → removeCallbackFuite de callback
Socket / Channelopen + onDispose → closeLa connexion reste ouverte

Abonnement aux capteurs via DisposableEffect

L’un des exemples les plus illustratifs de l’utilisation de DisposableEffect est le travail avec les capteurs de l’appareil (accéléromètre, gyroscope, magnétomètre). Les capteurs nécessitent une annulation d’enregistrement obligatoire à la fin, sinon ils continuent de consommer de l’énergie de la batterie et d’envoyer des données même après la fermeture de l’écran.

Exemple pratique : une application de mesure d’angle d’inclinaison. DisposableEffect(Unit) enregistre un écouteur d’accéléromètre lorsque le composant apparaît et annule l’enregistrement dans onDispose. Les données du capteur sont transmises à l’état via mutableStateOf, ce qui met à jour automatiquement l’interface. Si l’écran défile dans LazyColumn et que l’élément disparaît, onDispose se déclenche immédiatement — le capteur cesse d’envoyer des données pour cet élément.

Lors du changement de type de capteur (par exemple, d’accéléromètre à gyroscope), la clé sensorType change, onDispose annule l’ancien abonnement et le nouveau bloc DisposableEffect enregistre le nouveau capteur. Sans clés, vous devriez vérifier manuellement quel capteur était enregistré précédemment et appeler unregisterListener avec le bon écouteur — ce qui est sujet aux erreurs.

kotlin
@Composable
fun SensorReadingScreen(sensorType: Int) {
    val context = LocalContext.current
    val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
    var sensorValue by remember { mutableStateOf(0f) }
    
    DisposableEffect(sensorType) {
        val sensor = sensorManager.getDefaultSensor(sensorType)
        val listener = SensorEventListener { event, _ ->
            sensorValue = event.values[0]
        }
        sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL)
        
        onDispose {
            sensorManager.unregisterListener(listener)
        }
    }
    
    Text("Valeur : $sensorValue")
}

Enregistrement de BroadcastReceiver via DisposableEffect

BroadcastReceiver est un exemple classique d’API nécessitant une paire obligatoire register / unregister. Dans une application Compose, DisposableEffect est idéal pour enregistrer un récepteur pendant la durée de vie d’un écran spécifique. À l’entrée de l’écran, un BroadcastReceiver avec le IntentFilter nécessaire est enregistré ; à la sortie, il est automatiquement annulé dans onDispose.

Un scénario typique est la surveillance de l’état du réseau. DisposableEffect enregistre un récepteur pour ConnectivityManager qui notifie des changements de connectivité réseau. Lorsque le statut change (WiFi / données mobiles / pas de réseau), l’état du composable est mis à jour et l’interface affiche l’indicateur correspondant. Lorsque l’écran se ferme, onDispose garantit l’annulation de l’enregistrement — même si l’application passe en arrière-plan.

Pour les récepteurs utilisant ContextCompat.registerReceiver avec le flag RECEIVER_EXPORTED / RECEIVER_NOT_EXPORTED (Android 14+), l’utilisation de DisposableEffect devient obligatoire car le système exige une spécification explicite de la portée du récepteur. DisposableEffect garantit que la portée est limitée à la durée de vie de l’écran, ce qui correspond aux exigences de sécurité des versions récentes d’Android.

kotlin
@Composable
fun NetworkStatusBanner() {
    val context = LocalContext.current
    var isConnected by remember { mutableStateOf(true) }
    
    DisposableEffect(Unit) {
        val receiver = BroadcastReceiver { _, _ ->
            val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            isConnected = cm.getActiveNetwork() != null
        }
        IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).let { filter ->
            context.registerReceiver(receiver, filter)
        }
        
        onDispose {
            context.unregisterReceiver(receiver)
        }
    }
    
    if (!isConnected) { ... }
}

Erreurs courantes avec DisposableEffect

La première erreur critique est l’absence d’appel à onDispose. Le code à l’intérieur du bloc DisposableEffect doit appeler onDispose, sinon une erreur de compilation se produit. Cependant, les développeurs essaient parfois de contourner cela en plaçant onDispose dans une condition : if (condition) { onDispose { ... } }. Ce code compilera, mais onDispose ne sera pas enregistré si la condition n’est pas remplie — la ressource ne sera jamais libérée.

La deuxième erreur est l’utilisation de DisposableEffect pour des opérations asynchrones. DisposableEffect étant synchrone, vous ne pouvez pas écrire d’appels delay() ou await() à l’intérieur. Si vous avez besoin d’une initialisation asynchrone avec nettoyage ultérieur, utilisez une combinaison de LaunchedEffect (pour le chargement des données) et DisposableEffect (pour la configuration/le nettoyage des ressources natives), ou utilisez un mécanisme séparé avec rememberCoroutineScope.

La troisième erreur est la création de nouveaux objets à l’intérieur de DisposableEffect sans remember. Si des objets (capteur, écouteur, récepteur) sont créés à l’intérieur de l’effet à chaque appel et que les clés changent fréquemment, cela entraîne une création excessive d’objets et un ramassage de déchets. Il est préférable de déplacer la création d’objets dans remember ou remember { ... } en dehors de DisposableEffect, et de seulement les enregistrer et les annuler à l’intérieur de l’effet.

Questions fréquemment posées

Quelle est la différence entre DisposableEffect et LaunchedEffect ?

DisposableEffect fonctionne de manière synchrone et fournit onDispose pour un nettoyage explicite des ressources. LaunchedEffect fonctionne de manière asynchrone dans une coroutine et l’annule automatiquement lors du changement de clé ou de la sortie de la composition. Si une ressource nécessite l’appel d’une méthode de nettoyage (close, unregister, dispose) — utilisez DisposableEffect. Si l’opération est une fonction suspend — utilisez LaunchedEffect.

Le bloc onDispose est-il obligatoire dans DisposableEffect ?

Oui, onDispose est obligatoire — le compilateur Kotlin exige qu’il soit appelé à l’intérieur du bloc DisposableEffect. Si vous n’appelez pas onDispose, le code ne compilera pas. Cela a été fait intentionnellement pour empêcher les développeurs d’oublier et garantir que chaque ressource ouverte soit correctement fermée lors de la sortie de la composition.

Comment gérer les erreurs à l’intérieur de DisposableEffect ?

Utilisez try-catch à l’intérieur du bloc DisposableEffect. Si l’enregistrement de la ressource peut lever une exception (par exemple, capteur introuvable), enveloppez-le dans try et traitez l’erreur dans l’interface via un état séparé. onDispose doit être appelé indépendamment du succès de l’initialisation — placez-le dans un bloc finally ou à la fin de la section try.

Puis-je utiliser DisposableEffect pour m’abonner à un Flow ?

Non recommandé. Pour Flow, il est préférable d’utiliser LaunchedEffect avec collectLatest ou la méthode .collectAsState() avec Lifecycle.repeatOnLifecycle. DisposableEffect ne supporte pas les fonctions suspend, donc s’abonner à un Flow à l’intérieur nécessiterait de lancer une coroutine séparée via CoroutineScope, ce qui complique le code et augmente le risque de fuites.

Combien de DisposableEffects peut-il y avoir dans un même composable ?

Il n’y a pas de limites, mais il est recommandé de regrouper les ressources connexes dans un seul DisposableEffect avec plusieurs opérations à l’intérieur et un seul onDispose. Si les ressources sont indépendantes (par exemple, capteur et BroadcastReceiver), il est préférable de les diviser en DisposableEffects séparés avec des clés différentes — cela simplifie le débogage et évite la recréation non souhaitée de toutes les ressources lors du changement d’une clé.

Résumé

  • DisposableEffect — API d’effet secondaire Jetpack Compose pour une initialisation synchrone avec nettoyage garanti via onDispose.
  • onDispose — bloc obligatoire exécuté lors de la sortie de la composition ou du changement de clé, empêchant les fuites mémoire.
  • Clés — lors du changement de clé, onDispose est d’abord exécuté pour l’ancienne valeur, puis la réinitialisation a lieu avec la nouvelle.
  • Synchrone — DisposableEffect s’exécute de manière synchrone ; les fonctions suspend ne sont pas disponibles à l’intérieur.
  • Scénarios typiques — BroadcastReceiver, capteurs, écouteurs natifs, bibliothèques basées sur des callbacks, intégration AndroidView.
  • Fuites — DisposableEffect réduit le nombre de fuites de 60–70% par rapport à la gestion manuelle via des callbacks de cycle de vie.
  • Erreurs — principaux risques : appel conditionnel de onDispose, utilisation pour des opérations asynchrones, création d’objets sans remember à l’intérieur de l’effet.

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi