Back Press Handling est un mécanisme d'interception et de traitement du bouton Retour système dans Android, qui détermine quelle action est exécutée lorsqu'il est enfoncé. Selon Android Developers (2024), à partir d'Android 11, la méthode traditionnelle onBackPressed() a été remplacée par OnBackPressedDispatcher. La nouvelle API permet aux composants à n'importe quel niveau de la hiérarchie d'intercepter la pression, et pas seulement à l'Activity. Différence clé — prise en charge de plusieurs callbacks dans une chaîne avec priorités.
Points clés
Back Press Handling est un mécanisme système dans Android qui détermine ce qui se passe lorsque l'utilisateur appuie sur le bouton Retour matériel ou logiciel. Selon le contexte, la pression peut : fermer l'écran actuel et revenir au précédent, masquer le clavier, fermer un Drawer ou Bottom Sheet, quitter l'application si l'utilisateur se trouve sur l'écran racine.
Le comportement du bouton Retour a évolué avec chaque version d'Android. Android 10 a introduit la navigation gestuelle, Android 11 a présenté OnBackPressedDispatcher comme API standard, et Android 13 a apporté une meilleure prise en charge du predictive back gesture, où le système affiche une animation de transition avant d'exécuter réellement l'action. Google avance régulièrement vers un comportement prévisible et cohérent du Back Press sur tous les appareils.
La gestion correcte du bouton Retour est un élément UX critique dans une application Android. L'utilisateur s'attend à ce que la pression de Retour le ramène à l'écran précédent dans la pile de navigation, et non à ce que l'application se ferme de manière inattendue. Violer cette attente est l'une des principales causes d'avis négatifs et de faibles notes sur Google Play.
L'histoire de l'API Back Press dans Android reflète l'évolution générale de la plateforme : d'une méthode simple dans Activity à un système de callbacks flexible avec prise en charge du cycle de vie et de Compose. Examinons trois étapes de développement.
Depuis la toute première API Android, le bouton Retour était traité dans la méthode onBackPressed de la classe Activity. Le développeur surchargeait cette méthode et écrivait sa propre logique. Le problème était que Fragment et View ne pouvaient pas intercepter la pression — tout le contrôle passait par l'Activity. Cela conduisait à des Activity gonflées et à des chaînes if-else complexes pour déterminer qui devait gérer la pression.
Avec Activity 1.0.0 (AndroidX), Google a introduit OnBackPressedDispatcher. C'est un répartiteur central qui accepte les callbacks de tout composant — Activity, Fragment, Dialog, View personnalisée. Les callbacks sont enregistrés avec un ordre (via priority) et peuvent être ajoutés ou supprimés dynamiquement. OnBackPressedDispatcher est invoqué avant l'ancien onBackPressed, ce qui permet d'intercepter la pression avant que l'Activity ne la traite.
Android 13 a introduit le predictive back gesture — une animation système qui montre où la pression de Retour mènera avant que l'utilisateur ne termine le geste. Pour prendre en charge cette animation, les développeurs doivent utiliser OnBackPressedDispatcher et indiquer si le callback prend en charge l'animation système via la propriété isEnabled. Si le callback ne prend pas en charge l'animation prédictive, le système affiche une animation par défaut qui peut ne pas correspondre au contexte de l'application.
| API | SDK minimum | Prise en charge Fragment | Predictive Back |
|---|---|---|---|
| onBackPressed | API Level 1 | Via Activity | Non |
| OnBackPressedDispatcher | Activity 1.0.0 | Directe | Partielle |
| OnBackPressedDispatcher + lifecycle | Activity 1.3.0 | Lifecycle-aware | Complète |
OnBackPressedDispatcher est le cœur de la nouvelle API Back Press. Il gère une chaîne de callbacks, les invoquant dans l'ordre jusqu'à ce que le premier traite l'événement. Si aucun callback ne traite la pression, le répartiteur exécute l'action par défaut — appeler finish() pour une Activity ou popBackStack() pour le Navigation Component.
Un callback est enregistré via addCallback avec un LifecycleOwner et un objet OnBackPressedCallback. Le callback a une propriété isEnabled — si elle est définie sur false, le callback est ignoré. Pour la priorité, vous pouvez passer une valeur de 0 (la plus basse) à Integer.MAX_VALUE. L'API Fragment Activity Result utilise ce mécanisme pour l'enregistrement automatique des callbacks liés au cycle de vie.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callback = object : OnBackPressedCallback(enabled = true) {
override fun handleOnBackPressed() {
if (isDrawerOpen) { closeDrawer() }
else { isEnabled = false; onBackPressed() }
}
}
onBackPressedDispatcher.addCallback(this, callback)
}
Les callbacks sont automatiquement supprimés lorsque le LifecycleOwner passe à l'état DESTROYED. Cela résout l'ancien problème de fuites de callbacks lors de la rotation de l'écran. Si un callback est ajouté dans un Fragment, il est garanti d'être supprimé lorsque le Fragment est détruit. Pour désactiver temporairement un callback, utilisez la propriété isEnabled — vous pouvez la basculer sans suppression ni réenregistrement.
L'ordre d'appel est l'inverse de l'ordre d'ajout : le dernier callback ajouté reçoit le contrôle en premier. C'est logique car l'élément d'UI le plus imbriqué (par exemple, un Bottom Sheet dans un Fragment) doit traiter la pression avant son Fragment parent. Si le callback le plus profond ne traite pas la pression (isEnabled = false), le contrôle passe au suivant dans la chaîne.
L'API Fragment fournit sa propre intégration avec OnBackPressedDispatcher via la méthode requireActivity().onBackPressedDispatcher. Depuis Fragment 1.2.0, chaque Fragment peut enregistrer son propre callback, qui est automatiquement lié au cycle de vie du Fragment et supprimé lors de sa destruction.
L'enregistrement d'un callback dans un Fragment se fait dans onCreate, onViewCreated ou même dans la View elle-même — l'important est que le LifecycleOwner (Fragment) soit actif. Lorsque le Fragment passe à l'état STARTED, le callback est activé ; à STOPPED, il est désactivé. Cela garantit qu'un Fragment caché (dans ViewPager) ne traitera pas la pression de Retour.
class EditorFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
showDiscardDialog()
}
}
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
}
}
Les dialogues et BottomSheets interceptent automatiquement la pression de Retour pour se fermer. Si vous devez effectuer une action supplémentaire avant la fermeture — enregistrez un callback avec une priorité plus élevée. Important : si vous définissez setCancelable(false) sur un Dialog, le callback ne se déclenchera pas — c'est un comportement système.
Jetpack Compose fournit une API déclarative pour gérer le bouton Retour via la fonction composable BackHandler. BackHandler accepte enabled (état booléen) et onBack — un callback invoqué lors de la pression. Si enabled = false, la pression est transmise plus loin dans la chaîne.
BackHandler enregistre automatiquement un OnBackPressedCallback dans l'OnBackPressedDispatcher de l'Activity parente. Il respecte le cycle de vie du composable : en quittant la composition, le callback est supprimé. enabled peut être lié à l'état — par exemple, afficher une boîte de dialogue de confirmation uniquement si le formulaire contient des modifications non sauvegardées.
@Composable
fun EditScreen(hasUnsavedChanges: Boolean) {
BackHandler(enabled = hasUnsavedChanges) {
// Show confirmation dialog
}
Column {
TextField(value = ..., onValueChange = ...)
}
}
Le predictive back gesture dans Compose est pris en charge à partir de Compose 1.5.0. BackHandler gère automatiquement l'animation de transition système si elle est activée sur l'appareil. Pour une animation prédictive personnalisée, utilisez le modificateur predictiveBackHandler, qui renvoie la progression du geste de 0 à 1.
Back Press Handling semble simple, mais dans la pratique, les développeurs commettent un certain nombre d'erreurs systématiques. Examinons les problèmes les plus courants et leurs solutions basées sur les recommandations de Google et l'expérience de la communauté.
L'appel direct de finish() dans handleOnBackPressed peut entraîner une fermeture inattendue de l'application si des écrans d'arrière-plan se trouvent dans la pile de navigation. Vérifiez toujours NavController.backStack via le Navigation Component ou Coordinator avant de fermer l'Activity.
Si vous enregistrez un callback sans LifecycleOwner (en utilisant l'ancien addCallback sans paramètre), le callback vivra éternellement et peut provoquer une NullPointerException si l'Activity est déjà détruite. Utilisez toujours addCallback(this, callback) avec un LifecycleOwner.
Pour les fenêtres modales (Bottom Sheet, Dialog), définissez toujours isEnabled = true uniquement lorsque la fenêtre est visible. Utilisez addCallback avec un lambda qui vérifie l'état de la fenêtre. Le Navigation Component gère cela automatiquement pour NavHost.
Une double pression rapide de Retour peut entraîner un double appel de finish(). Utilisez un indicateur ou throttleLast pour vous protéger contre les appels répétés dans les 500 ms. Le Navigation Component gère cette situation nativement, mais dans les scénarios personnalisés, vous devez implémenter la protection manuellement.
private var lastBackPressTime = 0L
override fun handleOnBackPressed() {
val currentTime = System.currentTimeMillis()
if (currentTime - lastBackPressTime > 500) {
lastBackPressTime = currentTime
navigateBack()
}
}
Questions fréquentes
onBackPressed est devenu obsolète dans Android 11 car il ne fonctionne qu'au niveau de l'Activity. OnBackPressedDispatcher permet à tout composant (Fragment, Dialog, View) d'intercepter la pression via un mécanisme unique avec prise en charge du cycle de vie.
Le système Android convertit un geste de balayage depuis le bord en une pression de Retour système avant que l'application ne le reçoive. Au niveau d'OnBackPressedDispatcher, vous ne pouvez pas distinguer ces deux événements — les deux arrivent comme handleOnBackPressed.
Oui, le traitement d'OnBackPressedDispatcher est le même pour les appareils à trois boutons et ceux à navigation gestuelle. Le code d'interception ne dépend pas du type de navigation — le système lui-même convertit le geste en appel au répartiteur.
Activez le predictive back dans les Developer Options d'un émulateur Android 13+. Utilisez ADB : `adb shell settings put global enable_back_animation 1`. Après activation, l'animation système montrera un aperçu de la transition lors de la pression de Retour.
Vérifiez deux conditions : le LifecycleOwner doit être dans l'état STARTED ou RESUMED, et isEnabled du callback doit être true. Si les deux conditions sont remplies, assurez-vous que le callback a été ajouté au bon OnBackPressedDispatcher — utilisez requireActivity().onBackPressedDispatcher dans un Fragment.
Résumé
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.
Lisez aussi