Scoped Storage ist ein Dateisystemzugriffsmodell, das in Android 10 (API 29) eingeführt wurde und den willkürlichen Zugriff von Apps auf den gemeinsamen Speicher des Geräts einschränkt. Laut Google Android Developer Documentation (2024) ersetzt Scoped Storage das alte Berechtigungsmodell READ_EXTERNAL_STORAGE und WRITE_EXTERNAL_STORAGE und gewährt Apps nur Zugriff auf ihre eigenen Dateien in einer isolierten Sandbox und auf gemeinsame Sammlungen über MediaStore. Dieses Modell verbessert den Datenschutz der Benutzerdaten und verhindert die unbefugte Sammlung von Informationen durch Apps.
Wichtige Punkte
Scoped Storage ist eine architektonische Änderung in Android 10 (API 29), die die Art und Weise, wie Apps auf das Dateisystem des Geräts zugreifen, grundlegend verändert. Anstelle des vollständigen Zugriffs auf den gemeinsamen Speicher erhält die App nur Zugriff auf ihre eigene Sandbox und auf bestimmte Mediensammlungen über MediaStore.
Vor Scoped Storage konnte jede App mit der Berechtigung READ_EXTERNAL_STORAGE alle Dateien im gemeinsamen Speicher lesen — Fotos, Dokumente, Downloads, Daten anderer Apps. Dies schuf Datenschutzrisiken: nicht vertrauenswürdige Apps konnten ohne Wissen des Benutzers Informationen sammeln.
Laut Google I/O 2019 betrachten über 65% der Android-Nutzer den Datenschutz als kritischen Faktor bei der App-Wahl. Scoped Storage ist die direkte Antwort auf diese Nachfrage: Jede App arbeitet in einer isolierten Umgebung und erhält nur mit ausdrücklicher Zustimmung des Benutzers Zugriff auf Daten anderer Apps.
Wichtig: Scoped Storage war in Android 10 optional (die App konnte es über requestLegacyExternalStorage deaktivieren). Ab Android 11 (API 30) wurde Scoped Storage für alle Apps verbindlich, unabhängig vom target SDK.
Google führte Scoped Storage ein, um drei grundlegende Probleme des alten Dateizugriffsmodells zu lösen: Datenschutz, Berechtigungsverwaltung und Bereinigung von Restdateien.
Im alten Modell konnte eine App mit READ_EXTERNAL_STORAGE den gesamten gemeinsamen Speicher scannen und Metadaten von Benutzerdateien sammeln — geografische Koordinaten von Fotos, Dokumentnamen, Verzeichnisstrukturen. Scoped Storage eliminiert diese Möglichkeit: Selbst mit Berechtigung sieht die App nur die von ihr selbst erstellten Dateien und die vom Benutzer explizit über SAF ausgewählten Dateien.
Vor Scoped Storage konnten Apps nach der Deinstallation Dateien im gemeinsamen Speicher hinterlassen. Mit der Zeit sammelten sich Junk-Verzeichnisse an. Scoped Storage löst dies: Alle Dateien in der Sandbox der App werden mit der App gelöscht, und Dateien über MediaStore haben einen Besitzer und können vom System bereinigt werden.
| Aspekt | Altes Modell (Legacy) | Scoped Storage |
|---|---|---|
| Zugriff auf gemeinsamen Speicher | Vollständig (mit Berechtigung) | Nur eigene Sandbox |
| Mediendateien | Direkter Dateipfad | Über MediaStore-URI |
| Dateien anderer Apps | Immer zugänglich | Nur über SAF |
| Bereinigung bei Deinstallation | Dateien bleiben | Sandbox wird entfernt |
| Kontrolle des Benutzers | Minimal | Explizite Zustimmung pro Datei |
Scoped Storage ist Teil der gesamten Datenschutz-Strategie von Google in Android, die auch Berechtigungen „nur während der Nutzung", Kamera- und Mikrofonzugriffsindikatoren und das Privacy Dashboard umfasst.
Jede Android-App erhält ihr eigenes privates Verzeichnis, das über Context.getFilesDir() und context.getCacheDir() zugänglich ist. Für den Zugriff auf das externe (gemeinsame) private Verzeichnis wird Context.getExternalFilesDir() verwendet.
In Scoped Storage hat eine App vollen Zugriff auf ihr externes privates Verzeichnis ohne Berechtigungen. Dies ist der primäre Ort zum Speichern von Dateien, die für andere Apps nicht zugänglich sein sollen oder die die App für den eigenen Gebrauch erstellt.
import android.os.Environment
import java.io.File
import java.io.IOException
class FileManager {
fun saveToAppStorage(context: Context, fileName: String, data: ByteArray) {
val appDir = context.getExternalFilesDir(null)
?: return
val file = File(appDir, fileName)
file.writeBytes(data)
}
fun readFromAppStorage(context: Context, fileName: String): ByteArray? {
val appDir = context.getExternalFilesDir(null)
?: return null
val file = File(appDir, fileName)
return file.takeIf { it.exists() }?.readBytes()
}
}
Pfad zum externen privaten Verzeichnis: /storage/emulated/0/Android/data/{packageName}/files/. Ab Android 11 ist der direkte Dateipfad nicht verfügbar — nur über die API. Dies ist eine weitere Sicherheitsverbesserung von Scoped Storage.
MediaStore ist die primäre API für den Zugriff auf gemeinsame Mediendateien (Bilder, Videos, Audio) in Scoped Storage. Die App arbeitet nicht mit Dateipfaden, sondern mit content URIs, die vom MediaStore-Anbieter bereitgestellt werden.
MediaStore ist in drei Hauptsammlungen unterteilt: Images, Video und Audio. Jede Sammlung unterstützt CRUD-Operationen über ContentResolver. Zum Einfügen einer neuen Datei in die Images-Sammlung wird MediaStore.Images.Media verwendet; zum Abfragen vorhandener Dateien — query mit der entsprechenden URI.
import android.content.ContentValues
import android.provider.MediaStore
import android.os.Environment
import java.io.OutputStream
fun saveImageToGallery(context: Context, bitmap: Bitmap, title: String) {
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "$title.jpg")
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(
MediaStore.Images.Media.RELATIVE_PATH,
"${Environment.DIRECTORY_PICTURES}/MyApp"
)
}
val uri = context.contentResolver
.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
?: return
context.contentResolver.openOutputStream(uri)?.use { output: OutputStream ->
bitmap.compress(
Bitmap.CompressFormat.JPEG,
95,
output
)
}
}
Wichtig: MediaStore unterstützt keine beliebigen Dateipfade — nur content URIs. Der Versuch, über MediaStore.Files.getContentUri() einen Dateipfad aus einer URI zu erhalten, ergibt auf Android 11+ keinen direkten Pfad zur Datei. Verwenden Sie stattdessen ContentResolver.openInputStream() und openOutputStream() für die Arbeit mit Inhalten.
Storage Access Framework (SAF) ist eine API für den Zugriff auf beliebige Dateien und Verzeichnisse außerhalb der App-Sandbox. SAF bietet dem Benutzer eine Oberfläche zur Auswahl von Dateien oder Verzeichnissen, woraufhin die App eine content URI mit temporärem Zugriff erhält.
SAF wird für Szenarien verwendet, die nicht von MediaStore abgedeckt werden: Arbeiten mit beliebigen Dokumenten (PDF, ZIP, APK), Zugriff auf Verzeichnisse auf der SD-Karte, Import und Export von Dateien aus anderen Apps. Der Benutzer wählt explizit eine Datei über die Systemdateiauswahl aus — dies garantiert seine Zustimmung zum Zugriff.
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.provider.DocumentsContract
const val REQUEST_CODE_PICK_DIR = 1001
fun pickDirectory(activity: Activity) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
activity.startActivityForResult(intent, REQUEST_CODE_PICK_DIR)
}
fun handlePickResult(requestCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE_PICK_DIR && data != null) {
val treeUri: Uri = data.data ?: return
// Take persistent URI permission
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(treeUri, flags)
}
}
SAF gewährt der App Zugriff auf das vom Benutzer ausgewählte Verzeichnis bis zum Neustart des Geräts (bei Verwendung von takePersistableUriPermission). Dies ist die einzige Möglichkeit, auf Android 11+ Zugriff auf beliebige Dateien im gemeinsamen Speicher zu erhalten.
Die Migration einer bestehenden Android-App auf Scoped Storage erfordert Änderungen in mehreren Schlüsselbereichen. Google empfiehlt einen schrittweisen Ansatz mit Tests auf Android 11+.
Ersetzen Sie alle direkten File-Operationen im gemeinsamen Speicher durch ContentResolver.openInputStream / openOutputStream. Für Dateien in der App-Sandbox (getExternalFilesDir) funktionieren Dateipfade weiterhin.
Entfernen Sie READ_EXTERNAL_STORAGE und WRITE_EXTERNAL_STORAGE, falls sie für spezifische Szenarien nicht benötigt werden (z.B. Dateiverwaltung — dafür gibt es die spezielle Berechtigung MANAGE_EXTERNAL_STORAGE).
| Szenario | Alter Ansatz | Scoped Storage Ansatz |
|---|---|---|
| Foto speichern | File(path).writeBytes() | MediaStore + ContentResolver |
| PDF lesen | File(path).inputStream() | SAF ACTION_OPEN_DOCUMENT |
| Eigene Dateien | Environment.getExternalStorageDirectory() | context.getExternalFilesDir() |
| Cache | File(cacheDir).writeBytes() | context.cacheDir (unverändert) |
Testen Sie die App nach der Migration auf Android 11+ (API 30), indem Sie targetSdk = 30 oder höher in build.gradle setzen. Stellen Sie sicher, dass alle Dateioperationen im gemeinsamen Speicher über MediaStore oder SAF und nicht über direkte Dateipfade funktionieren.
import android.os.Build
fun isScopedStorage(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
}
fun compatReadFile(context: Context, uri: Uri): ByteArray? {
return if (isScopedStorage()) {
context.contentResolver
.openInputStream(uri)?.readBytes()
} else {
File(uri.path ?: return null)
.takeIf { it.exists() }
?.readBytes()
}
}
Laut Google Play Console (2024) laufen über 78% der aktiven Android-Geräte auf Android 10+ mit Scoped Storage. Die Migration ist ein obligatorischer Schritt für die Veröffentlichung von Updates im Google Play Store: Neue Apps müssen API 31+, Updates API 30+ anvisieren.
Häufig gestellte Fragen
Nein, ab Android 11 (API 30) ist Scoped Storage für alle Apps verbindlich. Das Flag requestLegacyExternalStorage, das in Android 10 verfügbar war, funktioniert auf API 30+ nicht. Der einzige Weg, um breiten Dateisystemzugriff zu erhalten, ist die Berechtigung MANAGE_EXTERNAL_STORAGE, die jedoch nur für Dateimanager und Antivirenprogramme vorgesehen ist.
MANAGE_EXTERNAL_STORAGE ist eine spezielle Berechtigung für Apps, die vollständigen Dateisystemzugriff benötigen (Dateimanager, Backup-Tools). Die Anfrage der Berechtigung öffnet einen Systembildschirm mit einer Warnung für den Benutzer. Bei der Veröffentlichung im Google Play Store muss ein Declaration-Formular über die Notwendigkeit dieser Berechtigung ausgefüllt werden.
In Scoped Storage auf Android 11+ kann kein direkter Dateipfad aus einer content URI abgerufen werden. Verwenden Sie stattdessen ContentResolver.openInputStream() zum Lesen und openOutputStream() zum Schreiben. Wenn Sie einen Dateipfad für die Kompatibilität mit einer Drittanbieterbibliothek benötigen, erstellen Sie eine Kopie der Datei in getCacheDir() und arbeiten Sie mit der Kopie.
FileProvider funktioniert weiterhin ohne Änderungen — er wird verwendet, um anderen Apps über content URIs Zugriff auf Dateien aus der App-Sandbox zu gewähren. Scoped Storage hat keine Auswirkungen auf FileProvider, da FileProvider auf der Ebene der content URIs arbeitet, nicht auf direkten Dateipfaden.
Überprüfen Sie Build.VERSION.SDK_INT: wenn >= Build.VERSION_CODES.Q (29), dann unterstützt das Gerät Scoped Storage. Auf Android 10 kann Scoped Storage jedoch über requestLegacyExternalStorage deaktiviert sein. Überprüfen Sie auf Android 11+, ob das Manifest-Flag requestLegacyExternalStorage gesetzt ist — wenn nicht, ist Scoped Storage aktiv.
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