ActivityResultLauncher ist eine Komponente der Android Activity Result API, die in Version Activity 1.2.0 der Bibliothek androidx.activity eingeführt wurde. Es ersetzt die veralteten Methoden startActivityForResult und onActivityResult, die seit der Gründung des Android SDK Teil davon waren. Laut Android Developers (2024) beseitigt die neue API die Probleme der engen Kopplung mit Activity und der fehlenden Typsicherheit. ActivityResultLauncher wird im Voraus registriert und verwendet einen Contract für die strenge Typisierung von Eingabe- und Ausgabedaten.
Wichtige Punkte
ActivityResultLauncher ist eine Klasse aus dem Paket androidx.activity.result, die einen typsicheren Mechanismus zum Starten einer Activity und zum Empfangen eines Ergebnisses bereitstellt. Der Launcher wird über die Methode registerForActivityResult erstellt, die zwei Parameter akzeptiert: Contract (beschreibt Eingabe- und Ausgabetypen) und ActivityResultCallback (Ergebnis-Handler). Nach der Registrierung ist der Launcher bereit, über die Methode launch aufgerufen zu werden.
Der Hauptunterschied zur alten API ist die Trennung von Registrierung und Start. Die Registrierung erfolgt in der Initialisierungsphase (Activity.onCreate oder Fragment.onCreate), der Callback wird einmal an den Launcher gebunden und wird garantiert ausgeführt, wenn das Ergebnis zurückkommt. Dies beseitigt das Problem, dass onActivityResult in einer unerwarteten Reihenfolge oder auf einer zerstörten Activity ausgelöst wurde.
ActivityResultLauncher unterstützt alle Szenarien, die zuvor über onActivityResult behandelt wurden: Kamera starten, Galerie, Kontakte anfordern, Berechtigungen und benutzerdefinierte Activities. Darüber hinaus ist die API erweiterbar: Entwickler können benutzerdefinierte Contracts für spezifische Datenaustauschszenarien zwischen Activities erstellen.
startActivityForResult war seit API Level 1 (2008) Teil des Android SDK und blieb über 12 Jahre lang die primäre Methode, um ein Ergebnis von einer Activity zu erhalten. Diese Methode hatte jedoch grundlegende Mängel, die Google in der Activity Result API behoben hat. Schauen wir uns die Hauptprobleme an und wie die neue API sie löst.
Die Methode startActivityForResult ist über requestCode an Activity und Fragment gebunden — eine beliebige ganze Zahl, die an onActivityResult übergeben wird. Der Entwickler musste den Code manuell mit der gestarteten Operation abgleichen, was zu Fehlern bei der Wiederverwendung von Code und Vererbung führte. ActivityResultLauncher eliminiert requestCode vollständig: Der Callback wird bei der Registrierung an einen bestimmten Launcher gebunden und nur für diesen aufgerufen.
Bei Konfigurationsänderungen (Bildschirmdrehung, Sprachwechsel) wurde die Activity neu erstellt und onActivityResult konnte fehlschlagen — der Callback ging verloren. Die Activity Result API speichert und stellt den Launcher-Zustand automatisch über SavedStateRegistry wieder her, sodass das Ergebnis auch nach der Neuerstellung der Activity garantiert empfangen wird.
Die alte API übergab das Ergebnis über Intent mit einem Bundle, wobei Schlüssel und Datentypen nicht vom Compiler überprüft wurden. Die Activity Result API verwendet Contract — eine generische Schnittstelle, die den Eingabedatentyp (I) und den Ergebnistyp (O) definiert. Typinkompatibilitätsfehler werden zur Compile-Zeit erkannt, nicht zur Laufzeit.
| Merkmal | startActivityForResult | ActivityResultLauncher |
|---|---|---|
| RequestCode | Manuelle Verwaltung erforderlich | Automatisch, nicht erforderlich |
| Typsicherheit | Nein | Generischer Contract |
| Speichern bei Drehung | Verloren | SavedStateRegistry |
| Minimale API | API Level 1 | Activity 1.2.0 |
| Verwendung in Compose | Nicht unterstützt | rememberLauncherForActivityResult |
Contract ist das Interface ActivityResultContract<I, O>, das definiert, wie eine Activity gestartet und wie das Ergebnis interpretiert wird. Google bietet eine Reihe integrierter Verträge für typische Szenarien, die die meisten Entwickleranforderungen abdecken.
StartIntentSenderForResult — ein grundlegender Vertrag zum Starten von IntentSender. Wird in Systemszenarien verwendet, z. B. bei der Autorisierung über Google Sign-In oder bei Zahlungen über Google Pay. Der Eingabeparameter ist PendingIntent, die Ausgabe ist ActivityResult mit Code und Intent.
RequestMultiplePermissions — ein Vertrag zum gleichzeitigen Anfordern mehrerer Berechtigungen unter Android 6.0+. Der Eingabeparameter ist ein String-Array mit Berechtigungsnamen, die Ausgabe ist Map<String, Boolean> mit dem Ergebnis jeder Anfrage. Früher erforderte dies manuelles Parsen in onRequestPermissionsResult mit Abgleich der Anforderungscodes.
TakePicture — ein Vertrag zum Aufnehmen eines Fotos über die Systemkamera. Die Eingabe ist ein Uri zum Speichern des Bildes, die Ausgabe ist Boolean (Erfolg). TakeVideo funktioniert ähnlich mit Video. Diese Verträge ersetzen das veraltete MediaStore.ACTION_IMAGE_CAPTURE mit instabilem Verhalten auf verschiedenen Geräten.
GetContent — ein Vertrag zum Auswählen von Inhalten über die Systemauswahl. Die Eingabe ist ein MIME-Typ (z. B. image/*), die Ausgabe ist ein Uri der ausgewählten Datei. OpenDocument unterscheidet sich durch die Unterstützung von Mehrfachauswahl und Filterung nach Dokumenttypen. Beide Verträge funktionieren über SAF (Storage Access Framework).
CreateDocument — ein Vertrag zum Erstellen eines neuen Dokuments über den Systemdialog. Der Benutzer wählt einen Namen und Ordner, das System gibt einen Uri zum Schreiben zurück. OpenDocumentTree bietet Zugriff auf ein gesamtes Verzeichnis — der Benutzer wählt einen Ordner und die App erhält einen tree-uri zum Lesen und Schreiben aller darin enthaltenen Dateien.
Das grundlegende Muster zur Verwendung von ActivityResultLauncher in klassischem Android besteht aus zwei Schritten: Registrierung über registerForActivityResult bei der Initialisierung und Aufruf von launch als Reaktion auf eine Benutzeraktion. Schauen wir uns ein typisches Beispiel zum Auswählen eines Bildes aus der Galerie an.
Registrieren Sie den Launcher im onCreate der Activity — dies stellt sicher, dass der Callback vor jedem möglichen Aufruf bereit ist. Registrieren Sie einen Launcher niemals unmittelbar vor dem Start — dies verstößt gegen den API-Vertrag und kann bei der Neuerstellung der Activity zum Verlust des Ergebnisses führen.
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 einem Fragment erfolgt die Registrierung in onCreate, onAttach oder in der Initialisierung in onCreateView. FragmentActivity übergibt den Launcher über die Eltern-Activity, sodass das Ergebnis innerhalb des Fragments verarbeitet wird, nicht in der Activity. Dies verbessert die Kapselung im Vergleich zu onActivityResult, wo alle Ergebnisse aller Fragments in einer einzigen Activity-Methode gesammelt wurden.
class ProfileFragment : Fragment() {
private val cameraLauncher =
registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
if (success) { updateProfilePhoto() }
}
fun takePhoto(photoUri: Uri) {
cameraLauncher.launch(photoUri)
}
}
Jetpack Compose bietet eine spezielle composable-Funktion für die Activity Result API — rememberLauncherForActivityResult. Im Gegensatz zum klassischen Ansatz wird der Launcher in Compose als Objekt erstellt, das über remember an den Lebenszyklus des Composables gebunden ist. Dies ermöglicht die Verwendung der Activity Result API vollständig in einem deklarativen Stil ohne direkten Zugriff auf Activity oder Fragment.
rememberLauncherForActivityResult nimmt einen Contract und einen Callback entgegen und gibt einen ActivityResultLauncher zurück. Der Launcher bleibt während der Rekomposition erhalten und wird beim Verlassen der Komposition automatisch gelöscht. Der launch-Aufruf erfolgt als Reaktion auf ein Ereignis — beispielsweise einen Button-Klick oder eine Zustandsänderung.
@Composable
fun PhotoPicker() {
val context = LocalContext.current
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.GetContent()
) { uri -> handleImage(uri) }
Button(onClick = { launcher.launch("image/*") }) {
Text("Foto auswählen")
}
}
Berechtigungsanfragen in Compose werden ebenfalls über rememberLauncherForActivityResult mit dem Contract RequestPermission oder RequestMultiplePermissions durchgeführt. Google empfiehlt die Verwendung von accompanist-permissions, aber intern verwendet es ebenfalls die Activity Result API. Zur Verfolgung des Berechtigungsstatus ist es praktisch, den Status in remember oder ViewModel zu speichern.
Die Activity Result API hat viele Probleme des alten Ansatzes beseitigt, aber eine unsachgemäße Verwendung kann zu neuen Arten von Fehlern führen. Schauen wir uns die häufigsten Probleme an und wie man sie vermeidet.
Die Registrierung des Launchers muss während der Komponenteninitialisierung erfolgen — im onCreate der Activity oder im Fragment-Initialisierer. Wenn Sie den Launcher innerhalb eines Lambda, Callbacks oder einer Coroutine registrieren, kann die Registrierung bei der Neuerstellung der Activity erneut durchgeführt werden und der alte Launcher verliert die Verbindung zum Ergebnis.
Jeder Launcher erhält einen eindeutigen Schlüssel zum Speichern des Zustands. Wenn Sie zwei Launcher mit demselben Contract in einer Komponente registrieren, kann SavedStateRegistry den Zustand des einen mit dem des anderen überschreiben. Android Studio warnt davor über die Lint-Regel UnnecessaryRegisterForActivityResult, aber es ist besser, die Eindeutigkeit manuell zu kontrollieren.
Der Benutzer kann die Aktion abbrechen — die System-Zurück-Taste drücken, die App minimieren oder zu einer anderen App wechseln. In diesem Fall erhält der Callback null oder ActivityResult mit RESULT_CANCELED. Überprüfen Sie das Ergebnis immer auf null, bevor Sie es verwenden, um NullPointerException zu vermeiden.
Wenn Ihre App häufig ähnliche Szenarien startet — z. B. einen Kontakt auswählen und Namen und Telefonnummer zurückgeben — erstellen Sie einen benutzerdefinierten Contract. Dies verbessert die Lesbarkeit des Codes und ermöglicht zentrale Änderungen an der Start- und Ergebnisverarbeitungslogik.
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) }
}
Häufig gestellte Fragen
Nein — ActivityResultLauncher benötigt einen Activity- oder Fragment-Kontext zur Registrierung. Verwenden Sie ViewModel nur zum Speichern von Zuständen und erstellen Sie den Launcher in der Activity oder im Fragment und übergeben Sie das Ergebnis an das ViewModel.
Activity Result API ist ab der Bibliothek activity-ktx 1.2.0 verfügbar. Das minimale SDK ist API Level 14 (Android 4.0), aber die meisten Verträge funktionieren nur ab API Level 19+.
Ein wiederholter Aufruf von launch vor Abschluss des ersten Vorgangs wird ignoriert. Die Activity Result API unterstützt keine parallelen Starts — warten Sie auf den Callback des ersten Vorgangs, bevor Sie einen neuen Aufruf tätigen.
Die Migration erfolgt durch Ersetzen des startActivityForResult-Aufrufs durch registerForActivityResult mit dem entsprechenden Contract. Entfernen Sie onActivityResult und behandeln Sie das Ergebnis im Launcher-Callback. Google stellt einen Migrationsleitfaden in der Android Developers-Dokumentation bereit.
Ja, viele Bibliotheken unterstützen die Integration über ActivityResultContracts. Beispielsweise verwendet ML Kit Barcode Scanner StartIntentSenderForResult zum Starten des Scanners. Prüfen Sie die Dokumentation der jeweiligen Bibliothek.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch