ActivityResultLauncher is een component van de Android Activity Result API, geïntroduceerd in versie Activity 1.2.0 van de androidx.activity-bibliotheek. Het vervangt de verouderde methoden startActivityForResult en onActivityResult, die sinds het begin deel uitmaakten van de Android SDK. Volgens Android Developers (2024) elimineert de nieuwe API de problemen van nauwe koppeling met Activity en het ontbreken van typeveiligheid. ActivityResultLauncher wordt vooraf geregistreerd en gebruikt Contract voor strikte typering van invoer- en uitvoergegevens.
Belangrijkste punten
ActivityResultLauncher is een klasse uit het pakket androidx.activity.result die een typeveilig mechanisme biedt voor het starten van Activity en het ontvangen van het resultaat. De Launcher wordt gemaakt via de methode registerForActivityResult, die twee parameters accepteert: Contract (beschrijft de invoer- en uitvoertypen) en ActivityResultCallback (de resultaatverwerker). Na registratie is de launcher klaar voor aanroep via de methode launch.
Het belangrijkste verschil met de oude API is de scheiding van registratie en lancering. Registratie vindt plaats in de initialisatiefase (Activity.onCreate of Fragment.onCreate), de callback wordt eenmalig aan de launcher gekoppeld en wordt gegarandeerd geactiveerd bij terugkeer van het resultaat. Dit elimineert het probleem dat onActivityResult in een onverwachte volgorde of op een vernietigde Activity werd geactiveerd.
ActivityResultLauncher ondersteunt alle scenario's die voorheen via onActivityResult werden afgehandeld: starten van camera, galerij, contacten aanvragen, machtigingen en aangepaste Activity's. Bovendien is de API uitbreidbaar: de ontwikkelaar kan eigen Contracten maken voor specifieke scenario's van gegevensuitwisseling tussen Activity's.
startActivityForResult maakte sinds API Level 1 (2008) deel uit van de Android SDK en bleef meer dan 12 jaar de belangrijkste manier om een resultaat van Activity te krijgen. Deze methode had echter fundamentele tekortkomingen die Google in de Activity Result API heeft verholpen. Laten we de belangrijkste problemen bekijken en hoe de nieuwe API ze oplost.
De methode startActivityForResult is gekoppeld aan Activity en Fragment via requestCode — een willekeurig geheel getal dat wordt doorgegeven aan onActivityResult. De ontwikkelaar moest handmatig de code koppelen aan de gestarte bewerking, wat leidde tot fouten bij hergebruik van code en overerving. ActivityResultLauncher elimineert requestCode volledig: de callback wordt in de registratiefase aan een specifieke launcher gekoppeld en wordt alleen daarvoor aangeroepen.
Bij configuratiewijzigingen (schermrotatie, taalwijziging) werd Activity opnieuw aangemaakt en kon onActivityResult niet worden geactiveerd — de callback ging verloren. Activity Result API slaat de status van de launcher automatisch op en herstelt deze via SavedStateRegistry, wat garandeert dat het resultaat wordt ontvangen, zelfs na het opnieuw aanmaken van Activity.
De oude API gaf het resultaat door via Intent met Bundle, waarbij sleutels en gegevenstypen niet door de compiler werden gecontroleerd. Activity Result API gebruikt Contract — een generieke interface die het type invoergegevens (I) en het type resultaat (O) definieert. Typefouten worden gedetecteerd in de compilatiefase, niet tijdens runtime.
| Kenmerk | startActivityForResult | ActivityResultLauncher |
|---|---|---|
| RequestCode | Handmatig beheer vereist | Automatisch, niet nodig |
| Typeveiligheid | Nee | Generic Contract |
| Behoud bij rotatie | Gaat verloren | SavedStateRegistry |
| Minimale API | API Level 1 | Activity 1.2.0 |
| Gebruik in Compose | Niet ondersteund | rememberLauncherForActivityResult |
Contract is een interface ActivityResultContract<I, O> die definieert hoe Activity te starten en hoe het resultaat te interpreteren. Google biedt een set ingebouwde contracten voor typische scenario's die de meeste behoeften van de ontwikkelaar dekken.
StartIntentSenderForResult is het basiscontract voor het starten van IntentSender. Gebruikt in systeemscenario's, bijvoorbeeld bij authenticatie via Google Sign-In of bij betaling via Google Pay. Invoerparameter — PendingIntent, uitvoer — ActivityResult met code en Intent.
RequestMultiplePermissions is het contract voor het tegelijkertijd aanvragen van meerdere machtigingen in Android 6.0+. Invoerparameter — een String-array met machtigingsnamen, uitvoer — Map<String, Boolean> met het resultaat van elke aanvraag. Vroeger vereiste dit handmatige parsing in onRequestPermissionsResult met het matchen van aanvraagcodes.
TakePicture is het contract voor het maken van foto's via de systeemcamera. Invoer — Uri waar de foto moet worden opgeslagen, uitvoer — Boolean (succes). TakeVideo werkt op dezelfde manier met video. Deze contracten vervangen de verouderde MediaStore.ACTION_IMAGE_CAPTURE met onstabiel gedrag op verschillende apparaten.
GetContent is het contract voor het selecteren van inhoud via de systeemkiezer. Invoer — MIME-type (bijv. image/*), uitvoer — Uri van het geselecteerde bestand. OpenDocument onderscheidt zich door ondersteuning voor meervoudige selectie en filtering op documenttypen. Beide contracten werken via SAF (Storage Access Framework).
CreateDocument is het contract voor het maken van een nieuw document via het systeemdialoogvenster. De gebruiker kiest een naam en map, het systeem retourneert Uri om te schrijven. OpenDocumentTree geeft toegang tot een hele directory — de gebruiker kiest een map en de applicatie ontvangt een tree-uri voor het lezen en schrijven van alle bestanden erin.
Het basispatroon voor het gebruik van ActivityResultLauncher in klassiek Android bestaat uit twee stappen: registratie via registerForActivityResult in de initialisatiefase en aanroep van launch als reactie op een gebruikersactie. Laten we een typisch voorbeeld van het selecteren van een afbeelding uit de galerij bekijken.
Registreer de launcher in onCreate van de Activity — dit garandeert dat de callback gereed is vóór elke mogelijke aanroep. Registreer nooit de launcher vlak voor lancering — dit schendt het API-contract en kan leiden tot verlies van resultaat bij het opnieuw aanmaken van de 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/*")
}
}
In Fragment vindt registratie plaats in onCreate, onAttach of initialisatie in onCreateView. FragmentActivity geeft de launcher door via de bovenliggende Activity, zodat het resultaat binnen de Fragment wordt verwerkt, niet in de Activity. Dit verbetert de inkapseling in vergelijking met onActivityResult, waar alle resultaten van alle Fragmenten in één methode van de Activity werden verzameld.
class ProfileFragment : Fragment() {
private val cameraLauncher =
registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
if (success) { updateProfilePhoto() }
}
fun takePhoto(photoUri: Uri) {
cameraLauncher.launch(photoUri)
}
}
Jetpack Compose biedt speciale composable-functionaliteit voor Activity Result API — rememberLauncherForActivityResult. In tegenstelling tot de klassieke benadering, wordt in Compose de launcher gemaakt als een object dat via remember aan de levenscyclus van de composable is gekoppeld. Dit maakt het mogelijk om de Activity Result API volledig declaratief te gebruiken zonder directe toegang tot Activity of Fragment.
rememberLauncherForActivityResult accepteert Contract en callback en retourneert ActivityResultLauncher. De launcher wordt bewaard bij recompositie en automatisch opgeruimd bij het verlaten van de compositie. De aanroep van launch vindt plaats als reactie op een gebeurtenis — bijvoorbeeld een knopdruk of statuswijziging.
@Composable
fun PhotoPicker() {
val context = LocalContext.current
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.GetContent()
) { uri -> handleImage(uri) }
Button(onClick = { launcher.launch("image/*") }) {
Text("Kies foto")
}
}
Machtigingen aanvragen in Compose gebeurt ook via rememberLauncherForActivityResult met het contract RequestPermission of RequestMultiplePermissions. Google raadt het gebruik van accompanist-permissions aan, maar onder de motorkap gebruikt het ook Activity Result API. Voor het controleren van de machtigingsstatus is het handig om de status op te slaan in remember of ViewModel.
Activity Result API heeft veel problemen van de oude benadering geëlimineerd, maar onjuist gebruik kan leiden tot nieuwe soorten fouten. Laten we de meest voorkomende problemen bekijken en hoe u ze kunt vermijden.
Registratie van de launcher moet plaatsvinden bij initialisatie van het component — in onCreate van Activity of initialisator van Fragment. Als u de launcher registreert binnen een lambda, callback of coroutine, kan bij het opnieuw aanmaken van Activity de registratie opnieuw worden uitgevoerd en verliest de oude launcher de verbinding met het resultaat.
Elke launcher krijgt een unieke sleutel voor statusopslag. Als u twee launchers met hetzelfde Contract in één component registreert, kan SavedStateRegistry de status van de ene over de andere heen schrijven. Android Studio waarschuwt hiervoor via de lint-regel UnnecessaryRegisterForActivityResult, maar het is beter om de uniciteit handmatig te controleren.
De gebruiker kan de actie annuleren — op de systeem-terugknop drukken, de app minimaliseren of overschakelen naar een andere app. In dit geval ontvangt de callback null of ActivityResult met RESULT_CANCELED. Controleer het resultaat altijd op null vóór gebruik om NullPointerException te voorkomen.
Als uw app vaak vergelijkbare scenario's start — bijvoorbeeld het selecteren van een contactpersoon met terugkeer van naam en telefoon — maak dan uw eigen Contract. Dit verbetert de leesbaarheid van de code en maakt het mogelijk om de logica van lancering en resultaatverwerking centraal te wijzigen.
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) }
}
Veelgestelde vragen
Nee — ActivityResultLauncher heeft Activity- of Fragment-context nodig voor registratie. Gebruik ViewModel alleen voor statusopslag en maak de launcher in Activity of Fragment en geef het resultaat door aan ViewModel.
Activity Result API is beschikbaar vanaf bibliotheek activity-ktx 1.2.0. Minimale SDK — API Level 14 (Android 4.0), maar de meeste contracten werken alleen op API Level 19+.
Herhaalde aanroep van launch vóór voltooiing van de eerste bewerking wordt genegeerd. Activity Result API ondersteunt geen parallelle lanceringen — wacht op de callback van de eerste bewerking vóór een nieuwe aanroep.
Migratie wordt uitgevoerd door de aanroep van startActivityForResult te vervangen door registerForActivityResult met het bijbehorende Contract. Verwijder onActivityResult en verwerk het resultaat in de callback van de launcher. Google biedt een migratiegids in de Android Developers-documentatie.
Ja, veel bibliotheken ondersteunen integratie via ActivityResultContracts. Bijvoorbeeld ML Kit Barcode Scanner gebruikt StartIntentSenderForResult om de scanner te starten. Raadpleeg de documentatie van de specifieke bibliotheek.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook