ActivityResultLauncher est un composant de l’API Activity Result d’Android, présenté dans la version Activity 1.2.0 de la bibliothèque androidx.activity. Il remplace les méthodes obsolètes startActivityForResult et onActivityResult, qui faisaient partie du SDK Android depuis sa création. Selon Android Developers (2024), la nouvelle API élimine les problèmes de couplage fort avec Activity et l’absence de sécurité de type. ActivityResultLauncher est enregistré à l’avance et utilise un Contract pour un typage strict des données d’entrée et de sortie.
Points clés
ActivityResultLauncher est une classe du paquet androidx.activity.result qui fournit un mécanisme type-safe pour lancer une Activity et recevoir un résultat. Le launcher est créé via la méthode registerForActivityResult, qui accepte deux paramètres : Contract (décrit les types d’entrée et de sortie) et ActivityResultCallback (gestionnaire de résultat). Après l’enregistrement, le launcher est prêt à être appelé via la méthode launch.
La différence clé avec l’ancienne API est la séparation de l’enregistrement et du lancement. L’enregistrement est effectué lors de l’initialisation (Activity.onCreate ou Fragment.onCreate), le callback est lié au launcher une fois et est garanti de se déclencher lorsque le résultat revient. Cela élimine le problème où onActivityResult se déclenchait dans un ordre inattendu ou sur une Activity détruite.
ActivityResultLauncher prend en charge tous les scénarios qui étaient auparavant traités via onActivityResult : lancer l’appareil photo, la galerie, demander des contacts, des permissions et des Activities personnalisées. De plus, l’API est extensible : les développeurs peuvent créer des Contracts personnalisés pour des scénarios spécifiques d’échange de données entre Activities.
startActivityForResult faisait partie du SDK Android depuis API Level 1 (2008) et est resté le principal moyen d’obtenir un résultat d’une Activity pendant plus de 12 ans. Cependant, cette méthode présentait des défauts fondamentaux que Google a corrigés dans Activity Result API. Examinons les principaux problèmes et comment la nouvelle API les résout.
La méthode startActivityForResult est liée à Activity et Fragment via requestCode — un entier arbitraire passé à onActivityResult. Le développeur faisait correspondre manuellement le code à l’opération lancée, ce qui entraînait des erreurs lors de la réutilisation du code et de l’héritage. ActivityResultLauncher élimine complètement requestCode : le callback est lié à un launcher spécifique lors de l’enregistrement et n’est invoqué que pour celui-ci.
Lors des changements de configuration (rotation de l’écran, changement de langue), l’Activity était recréée et onActivityResult pouvait ne pas se déclencher — le callback était perdu. Activity Result API sauvegarde et restaure automatiquement l’état du launcher via SavedStateRegistry, garantissant la réception du résultat même après la recréation de l’Activity.
L’ancienne API transmettait le résultat via Intent avec un Bundle, où les clés et les types de données n’étaient pas vérifiés par le compilateur. Activity Result API utilise Contract — une interface générique qui définit le type de données d’entrée (I) et le type de résultat (O). Les erreurs d’incompatibilité de type sont détectées à la compilation, pas à l’exécution.
| Caractéristique | startActivityForResult | ActivityResultLauncher |
|---|---|---|
| RequestCode | Gestion manuelle requise | Automatique, non requis |
| Sécurité de type | Non | Contract générique |
| Sauvegarde lors de rotation | Perdue | SavedStateRegistry |
| API minimale | API Level 1 | Activity 1.2.0 |
| Utilisation dans Compose | Non supporté | rememberLauncherForActivityResult |
Contract est l’interface ActivityResultContract<I, O>, qui définit comment lancer une Activity et comment interpréter le résultat. Google fournit un ensemble de contrats intégrés pour les scénarios typiques couvrant la plupart des besoins des développeurs.
StartIntentSenderForResult — un contrat de base pour lancer IntentSender. Utilisé dans des scénarios système, par exemple lors de l’autorisation via Google Sign-In ou des paiements via Google Pay. Le paramètre d’entrée est PendingIntent, la sortie est ActivityResult avec le code et Intent.
RequestMultiplePermissions — un contrat pour demander plusieurs permissions simultanément sur Android 6.0+. Le paramètre d’entrée est un tableau de String avec les noms des permissions, la sortie est Map<String, Boolean> avec le résultat de chaque demande. Auparavant, cela nécessitait une analyse manuelle dans onRequestPermissionsResult avec correspondance des codes de demande.
TakePicture — un contrat pour prendre une photo via l’appareil photo système. L’entrée est un Uri où sauvegarder l’image, la sortie est Boolean (succès). TakeVideo fonctionne de manière similaire avec la vidéo. Ces contrats remplacent l’obsolète MediaStore.ACTION_IMAGE_CAPTURE au comportement instable sur différents appareils.
GetContent — un contrat pour sélectionner du contenu via le sélecteur système. L’entrée est un type MIME (par exemple image/*), la sortie est un Uri du fichier sélectionné. OpenDocument diffère en supportant la sélection multiple et le filtrage par type de document. Les deux contrats fonctionnent via SAF (Storage Access Framework).
CreateDocument — un contrat pour créer un nouveau document via la boîte de dialogue système. L’utilisateur choisit un nom et un dossier, le système retourne un Uri pour l’écriture. OpenDocumentTree fournit un accès à un répertoire entier — l’utilisateur sélectionne un dossier et l’application reçoit un tree-uri pour lire et écrire tous les fichiers à l’intérieur.
Le modèle de base pour utiliser ActivityResultLauncher dans Android classique comprend deux étapes : l’enregistrement via registerForActivityResult lors de l’initialisation et l’appel de launch en réponse à une action de l’utilisateur. Examinons un exemple typique de sélection d’une image depuis la galerie.
Enregistrez le launcher dans le onCreate de l’Activity — cela garantit que le callback est prêt avant tout appel potentiel. N’enregistrez jamais un launcher juste avant de le lancer — cela viole le contrat de l’API et peut entraîner une perte du résultat lors de la recréation de l’Activity.
class MainActivity : AppCompatActivity() {
private val pickImageLauncher =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
uri?.let { binding.imageView.setImageURI(it) }
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
pickImageLauncher.launch("image/*")
}
}
Dans un Fragment, l’enregistrement est effectué dans onCreate, onAttach ou lors de l’initialisation dans onCreateView. FragmentActivity transmet le launcher via l’Activity parente, donc le résultat est traité à l’intérieur du Fragment, pas dans l’Activity. Cela améliore l’encapsulation par rapport à onActivityResult, où tous les résultats de tous les Fragments étaient rassemblés dans une seule méthode d’Activity.
class ProfileFragment : Fragment() {
private val cameraLauncher =
registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
if (success) { updateProfilePhoto() }
}
fun takePhoto(photoUri: Uri) {
cameraLauncher.launch(photoUri)
}
}
Jetpack Compose fournit une fonction composable spéciale pour Activity Result API — rememberLauncherForActivityResult. Contrairement à l’approche classique, dans Compose le launcher est créé comme un objet lié au cycle de vie du composable via remember. Cela permet d’utiliser Activity Result API entièrement dans un style déclaratif sans accès direct à Activity ou Fragment.
rememberLauncherForActivityResult prend un Contract et un callback, retournant un ActivityResultLauncher. Le launcher est préservé lors de la recomposition et automatiquement nettoyé en quittant la composition. L’appel de launch se produit en réponse à un événement — par exemple, un clic sur un bouton ou un changement d’état.
@Composable
fun PhotoPicker() {
val context = LocalContext.current
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.GetContent()
) { uri -> handleImage(uri) }
Button(onClick = { launcher.launch("image/*") }) {
Text("Choisir une photo")
}
}
Les demandes de permissions dans Compose sont également effectuées via rememberLauncherForActivityResult avec le contrat RequestPermission ou RequestMultiplePermissions. Google recommande d’utiliser accompanist-permissions, mais en interne il utilise aussi Activity Result API. Pour suivre l’état des permissions, il est pratique de stocker l’état dans remember ou ViewModel.
Activity Result API a éliminé de nombreux problèmes de l’ancienne approche, mais une utilisation incorrecte peut entraîner de nouveaux types d’erreurs. Examinons les problèmes les plus courants et comment les éviter.
L’enregistrement du launcher doit être effectué lors de l’initialisation du composant — dans le onCreate de l’Activity ou l’initialiseur du Fragment. Si vous enregistrez le launcher dans une lambda, un callback ou une coroutine, lors de la recréation de l’Activity l’enregistrement peut être effectué à nouveau et l’ancien launcher perdra la connexion avec le résultat.
Chaque launcher reçoit une clé unique pour la sauvegarde d’état. Si vous enregistrez deux launchers avec le même Contract dans un composant, SavedStateRegistry peut écraser l’état de l’un par celui de l’autre. Android Studio avertit de cela via la règle lint UnnecessaryRegisterForActivityResult, mais il est préférable de contrôler l’unicité manuellement.
L’utilisateur peut annuler l’action — appuyer sur le bouton retour système, minimiser l’application ou passer à une autre application. Dans ce cas, le callback recevra null ou ActivityResult avec RESULT_CANCELED. Vérifiez toujours si le résultat est null avant de l’utiliser pour éviter NullPointerException.
Si votre application lance fréquemment des scénarios similaires — par exemple, sélectionner un contact et retourner un nom et un téléphone — créez un Contract personnalisé. Cela améliore la lisibilité du code et permet des modifications centralisées de la logique de lancement et de traitement des résultats.
class PickContactContract : ActivityResultContract<Void, ContactData?>() {
override fun createIntent(context: Context, input: Void?) =
Intent(Intent.ACTION_PICK).setType(ContactsContract.Contacts.CONTENT_TYPE)
override fun parseResult(resultCode: Int, intent: Intent?) =
intent?.data?.let { queryContact(it) }
}
Questions fréquentes
Non — ActivityResultLauncher nécessite un contexte Activity ou Fragment pour l’enregistrement. Utilisez ViewModel uniquement pour stocker l’état, et créez le launcher dans Activity ou Fragment et passez le résultat au ViewModel.
Activity Result API est disponible à partir de la bibliothèque activity-ktx 1.2.0. Le SDK minimum est API Level 14 (Android 4.0), mais la plupart des contrats fonctionnent uniquement sur API Level 19+.
Un appel répété de launch avant la fin de la première opération sera ignoré. Activity Result API ne prend pas en charge les lancements parallèles — attendez le callback de la première opération avant un nouvel appel.
La migration se fait en remplaçant l’appel startActivityForResult par registerForActivityResult avec le Contract approprié. Supprimez onActivityResult et traitez le résultat dans le callback du launcher. Google fournit un guide de migration dans la documentation Android Developers.
Oui, de nombreuses bibliothèques prennent en charge l’intégration via ActivityResultContracts. Par exemple, ML Kit Barcode Scanner utilise StartIntentSenderForResult pour lancer le scanner. Consultez la documentation de la bibliothèque spécifique.
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