Photo Picker: Medien auswählen ohne Berechtigungslast
Der Photo Picker erlaubt Nutzern, gezielt Medien freizugeben. Deine App erhält nur die ausgewählten Dateien – ganz ohne breite Speicherrechte.
Der Zugriff auf Fotos und Videos gehört zu den datenschutzsensibelsten Aktionen, die eine Android-App durchführen kann. Jahrelang mussten Entwickler dafür pauschale Speicherberechtigungen anfragen – ein Ansatz, der Nutzern zu viel Kontrolle entzog und regelmäßig für Misstrauen sorgte. Der Photo Picker löst dieses Spannungsfeld elegant: Er delegiert die Medienauswahl vollständig an das Betriebssystem und gibt deiner App ausschließlich das, was der Nutzer bewusst freigegeben hat.
Was ist das?
Der Photo Picker ist eine systemeigene Android-UI-Komponente, die seit Android 13 (API 33) fest im Betriebssystem verankert ist. Auf Geräten mit Android 11 und 12 liefert ein automatisches Backport-Modul über Google Play Services dieselbe Oberfläche. Noch ältere Geräte können über die Jetpack-Bibliothek androidx.activity:activity einen kompatiblen Fallback erhalten, der intern auf einen ACTION_GET_CONTENT-Intent zurückfällt.
Die Kernidee ist eine klare Verantwortungsteilung: Nicht deine App zeigt eine Medienauswahl-UI, sondern Android selbst. Der Nutzer wählt Fotos oder Videos aus; deine App empfängt nur die URIs der ausgewählten Elemente – und das auch nur temporär, solange der Task aktiv ist. Keine dauerhafte Einwilligung, keine breit gefassten READ_MEDIA_IMAGES-Berechtigungen.
Im Android-Sicherheitsmodell markiert das einen bedeutsamen Fortschritt. Statt auf eine Gesamtberechtigung für die Medienbibliothek zu hoffen, reicht eine einzige, bewusste Nutzerinteraktion. Das ist nicht nur datenschutzfreundlicher, sondern senkt auch die Hürde für den Nutzer, der App überhaupt zu vertrauen – was sich direkt in besseren Conversion-Rates bei Permission-Dialogen niederschlägt.
Wie funktioniert es?
Technisch basiert der Photo Picker auf dem ActivityResult-API von Jetpack. Du registrierst einen Launcher mit einem passenden ActivityResultContract:
PickVisualMedia– für die Auswahl eines einzelnen Fotos oder VideosPickMultipleVisualMedia– für eine Mehrfachauswahl, optional mit einer Maximalanzahl
Beim Start des Launchers übergibst du einen Request-Typ, der den angezeigten Medienfilter steuert:
PickVisualMedia.ImageOnly– ausschließlich BilderPickVisualMedia.VideoOnly– ausschließlich VideosPickVisualMedia.ImageAndVideo– beides ohne EinschränkungPickVisualMedia.SingleMimeType("image/gif")– ein exakter MIME-Typ
Das System öffnet daraufhin die Picker-UI des Betriebssystems. Wählt der Nutzer Medien aus, liefert der Launcher eine Uri zurück (oder eine List<Uri> bei Mehrfachauswahl). Schließt der Nutzer den Picker ohne Auswahl, kommt null bzw. eine leere Liste.
Die zurückgegebenen URIs sind temporär gültig: Sie sind an den Prozess und den Task gebunden. Für eine dauerhafte Referenz – etwa in einer Room-Datenbank oder einem Datei-Cache – musst du den Inhalt sofort in das interne App-Verzeichnis kopieren oder ContentResolver.takePersistableUriPermission() aufrufen, sofern der Content Provider das unterstützt.
In der Praxis
Hier ein vollständiges Beispiel in Kotlin, das sowohl in einem Fragment als auch in einer Activity funktioniert:
// Launcher für einzelne Bildauswahl registrieren
val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (uri != null) {
// URI sofort verarbeiten – sie ist nur temporär gültig
viewModel.onImageSelected(uri)
}
}
// Picker starten – nur Bilder anzeigen
pickMedia.launch(PickVisualMedia.ImageOnly)
Für eine Mehrfachauswahl mit maximal fünf Elementen:
val pickMultiple = registerForActivityResult(
ActivityResultContracts.PickMultipleVisualMedia(maxItems = 5)
) { uris ->
if (uris.isNotEmpty()) {
viewModel.onImagesSelected(uris)
}
}
pickMultiple.launch(PickVisualMedia.ImageAndVideo)
Häufige Stolperfalle: URI-Lebensdauer
Der klassische Fehler ist, eine URI in SharedPreferences oder als String-Feld in einer Room-Entity zu speichern und beim nächsten App-Start damit auf das Bild zugreifen zu wollen. Das schlägt mit einer SecurityException fehl, weil die temporäre Berechtigung abgelaufen ist.
Lösung A – Datei kopieren: Unmittelbar nach der Auswahl den Dateiinhalt per ContentResolver.openInputStream(uri) lesen und in context.filesDir ablegen. Danach speicherst du nur noch den internen Pfad.
Lösung B – Persistable URI Permission: Rufe nach der Auswahl contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) auf. Nicht jeder Content Provider gewährt das; prüfe zuvor mit checkUriPermission, ob das Flag FLAG_GRANT_PERSISTABLE_URI_PERMISSION im Intent gesetzt war.
Backward Compatibility
Auf Geräten mit Android 11 oder 12 liefert das Jetpack-Backport denselben Photo Picker, sofern Google Play Services aktuell ist. Für Geräte unter API 30 – falls du diese noch unterstützt – ist READ_EXTERNAL_STORAGE weiterhin nötig. Prüfe daher im Repository oder ViewModel, ob die Legacy-Berechtigung angefragt werden muss:
fun needsLegacyPermission(): Boolean =
Build.VERSION.SDK_INT < Build.VERSION_CODES.R
Fordere die Berechtigung nur in diesem Fall an. Die meisten modernen Apps können Geräte unter API 28 oder 29 bereits aus ihrem minSdk streichen und diesen Zweig komplett entfernen.
Fazit
Der Photo Picker ist das bevorzugte Werkzeug, sobald deine App Bilder oder Videos vom Gerät des Nutzers benötigt. Er eliminiert die Notwendigkeit breiter Speicherberechtigungen, schützt die Privatsphäre und vereinfacht das Permission-Handling erheblich. Öffne jetzt deine bestehende App und prüfe, ob du noch auf READ_MEDIA_IMAGES, einen selbst gebauten Galerie-Intent oder ACTION_GET_CONTENT setzt – und ersetze das durch PickVisualMedia. Teste das Ergebnis anschließend auf einem emulierten API-30-Gerät, und verifiziere im Code-Review, dass keine URI dauerhaft ohne takePersistableUriPermission oder sofortiges Kopieren gespeichert wird. Wer diesen Kreislauf einmal durchgespielt hat, versteht, wie Android Privacy-by-Design in der Praxis aussieht.