Zum Hauptinhalt springen

Android-Scoped Speicher in Apps mit Capacitor

Android gescoperte Speicherung in Capacitor-Anwendungen erklärt: Was änderte sich in Android 10 bis 14, welche Berechtigungen funktionieren noch, MediaStore, SAF und sichere Verzeichnisse.

Artikel Credits

Martin Donadieu

Autor

Valeria

Rezensent

Jordan

Redakteur

Android gescoperte Speicherung in Capacitor-Anwendungen

Die gescoperte Speicherung in Capacitor-Anwendungen beschränkt, was eine Anwendung außerhalb ihrer eigenen Ordner lesen und schreiben kann. Seit Android 10 und vollständig seit Android 11 kann eine Anwendung ihre privaten Verzeichnisse frei verwenden, Dateien in gemeinsamen Sammlungen über MediaStore hinzufügen und für alles andere die Benutzung des Speicherdienstes erfordern. Diese Anleitung erklärt, was sich änderte, Version für Version, welche Capacitor Directory Werte noch funktionieren und wie Dateien auf Capacitor 8 unterstützter Weise gespeichert, importiert und exportiert werden können.

Was sich änderte, Version für Version

Android API Ein wichtiger Wechsel für Capacitor-Anwendungen
10 29 Scoped storage introduced. Apps can opt out with requestLegacyExternalStorage="true" während sie sich auf API 29 richten
11 30 Gespeicherte Daten werden für Apps ab Version 30 eingeschränkt. requestLegacyExternalStorage ignoriert. WRITE_EXTERNAL_STORAGE erhält keine Zugriffsrechte mehr. MANAGE_EXTERNAL_STORAGE eingeführt. Der Baumpicker kann den Download-Root oder den Volumen-Root nicht auswählen.
12 31-32 Keine große Änderung im Speicherbereich
13 33 READ_EXTERNAL_STORAGE ersetzt durch READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIOerhält Zugriff auf Dokumente anderer Anwendungen ohne Berechtigung
14 34 Teilweise Zugriff auf Fotos mit READ_MEDIA_VISUAL_USER_SELECTED
15 und 16 35-36 Kein neuer Speichermodell, Play-Politik für Foto- und Videoberechtigungen gilt

Capacitor 8 Ziele API 36, so läuft Ihre App immer mit vollem gescoptem Speicher auf modernen Geräten. Es gibt keine Flagge, um das alte Verhalten zurückzubekommen.

Was ist bei älteren Capacitor code kaputt gegangen

Code geschrieben für Capacitor 2 oder 3, oder portiert von Cordova, funktioniert oft stillschweigend auf neuen Geräten:

  • Filesystem.writeFile({ directory: Directory.Documents }) oder Directory.ExternalStorage zu "auf das Telefon speichern". Bei Android 11+ zeigen diese Pfade auf den gemeinsamen Speicher, und Schreibvorgänge scheitern oder der Datei kann nach einem Neustart nicht mehr gelesen werden.
  • Zugriff auf beliebige Pfade wie /storage/emulated/0/Download/file.pdf aus einem von einem Benutzer eingegebenen Pfad oder von einem Server empfangen.
  • Anfrage READ_EXTERNAL_STORAGE um die Dokumente des Benutzers aufzulisten. Bei Android 13+ hat die Berechtigung für nicht-medienbezogene Dateien keinen Effekt.
  • Mit Filesystem.requestPermissions() und vorausgesetzt granted bedeutet, dass die App überall schreiben kann.
  • Dateipfade aus einem content:// URI, das von einem Picker zurückgegeben wird.

Was noch ohne jede Erlaubnis funktioniert:

Die eigenen Verzeichnisse der App bleiben von der gescannten Speicherung unberührt und benötigen keine Erlaubnis:

Capacitor Directory Android-Pfad Zu sehen für den Benutzer Bei Deinstallation entfernt
Data, Library, LibraryNoCloud /data/data/<package>/files Nein Ja
Cache /data/data/<package>/cache Nein Ja
External /storage/emulated/0/Android/data/<package>/files Nur über USB oder einige Dateimanager Ja
ExternalCache /storage/emulated/0/Android/data/<package>/cache Gleich Ja
Documents Geteilt Documents Ordner Ja Nein
ExternalStorage Geteilte Speicherwurzel Ja Nein

Die ersten vier Zeilen sind sicher. Documents und ExternalStorage are the legacy rows: since Android 11 your app can only see files there that it created itself, and after a reinstall it loses access even to those. Treat them as unsupported for new code.

External ist nützlich für große Dateien (Offline-Video, Karten) weil sie auf einem größeren Partition leben kann, und Dateien dort sind nicht Teil des Auto-Backup. Android/dataso ist es in der Praxis noch privat.

Dateien, die der Benutzer finden kann: MediaStore

Wenn der Benutzer auf eine Rechnung klickt, erwarten sie sie im Downloads-Ordner. Auf Android 10+ können Apps Dateien ohne jede Erlaubnis in die gemeinsamen Downloads, Pictures, Movies, Music und Documents Datenbanken über MediaStore ohne jede Erlaubnis.

@capgo/capacitor-file-sharer umhüllt dies mit save():

bun add @capgo/capacitor-file-sharer
bunx cap sync
import { FileSharer } from '@capgo/capacitor-file-sharer';
import { Filesystem, Directory } from '@capacitor/filesystem';

const { uri } = await Filesystem.getUri({ path: 'exports/invoice-1042.pdf', directory: Directory.Data });

const result = await FileSharer.save({
  filename: 'invoice-1042.pdf',
  path: uri,
  contentType: 'application/pdf',
  android: {
    saveDirectory: 'downloads',     // 'downloads' | 'pictures' | 'movies' | 'music' | 'documents'
    relativePath: 'MyApp/Invoices', // subfolder, Android 10+
  },
});

console.log(result.uri); // content:// URI of the saved file

On iOS öffnet die gleiche Anrufung das Teilen-Sheet, wobei der Benutzer "Speichern in Dateien" auswählt, so dass ein code Pfad beide Plattformen abdeckt.

Auf Android 9 und niedriger benötigt die Schreibzugriff auf gemeinsame Speicher noch WRITE_EXTERNAL_STORAGE. Erkläre es nur für diese Versionen:

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28" />

Capacitor 8 unterstützt Android 7 (API 24) und höher, also wenn Sie immer noch Geräte mit Android 7 bis 9 ausliefern, behalten Sie diese Zeile bei.

Fotos und Videos

Für Medien bevorzugen Sie die Plattform-Flüsse gegenüber dem Schreiben von Dateien selbst. @capacitor/camera schreibt in die Galerie mit saveToGallery: true, und benötigt nur noch Legacy-Speicherberechtigungen auf alten Android-Versionen. Siehe unsere Anleitung zu dem Aufnehmen und Bearbeiten von Fotos in Capacitor.

Lesen Sie Benutzerdateien: Picker anstatt Berechtigungen

Der Scoped Storage erwartet, dass der Benutzer Dateien auswählt. Der Android-Picker gibt eine content:// URI zurück, die Ihr App auch lesen kann, obwohl sie keine Speicherberechtigung hat.

import { CapgoFilePicker } from '@capgo/capacitor-file-picker';
import { Filesystem, Directory } from '@capacitor/filesystem';

const { files } = await CapgoFilePicker.pickFiles({ types: ['application/pdf'], limit: 1 });
const picked = files[0];

// Copy into app storage right away: picker grants can expire
await Filesystem.copy({
  from: picked.path!,
  to: `imports/${picked.name}`,
  toDirectory: Directory.Data,
});

Für Fotos und Videos @capgo/capacitor-file-picker's pickMedia und @capacitor/camera's chooseFromGallery Verwenden Sie auf Android 13+ den Android Photo Picker, der keine Berechtigung benötigt.

Vermeiden Sie die Berechtigungen READ_MEDIA_*

Google Play erlaubt die Foto- und Videoberechtigungen nur READ_MEDIA_IMAGES und READ_MEDIA_VIDEO für Apps, deren Hauptzweck eine breite Zugriffsmöglichkeit auf das Galerie-Verzeichnis benötigt, wie z.B. Galerie-Apps oder Foto-Editor-Apps. Alle anderen müssen den Picker verwenden und dies im Play Console deklarieren. Wenn ein Plugin, das Sie verwenden, diese Berechtigungen deklariert, entfernen Sie sie aus dem kombinierten Manifest:

<uses-permission
    android:name="android.permission.READ_MEDIA_IMAGES"
    tools:node="remove" />
<uses-permission
    android:name="android.permission.READ_MEDIA_VIDEO"
    tools:node="remove" />

Hinzufügen xmlns:tools="http://schemas.android.com/tools" zum <manifest> erstes Mal.

Ordnerzugriff mit der Storage Access Framework

Einige Apps benötigen einen ständigen Zugriff auf einen Ordner: eine Notizen-App, die sich mit einem Benutzerordner synchronisiert, ein Exportordner für eine Feld-App. Die Storage Access Framework (SAF) ermöglicht es dem Benutzer, einen Ordner zu genehmigen ACTION_OPEN_DOCUMENT_TREE:

const { path } = await CapgoFilePicker.pickDirectory();
// On Android: a content://com.android.externalstorage.documents/tree/... URI

Drei Dinge zu beachten:

  1. Das Ergebnis ist ein Baum-URI, nicht ein Pfad. Sie können es nicht weitergeben an Filesystem.writeFileDateien darin werden über oder erstellt und gelesen DocumentsContract oder DocumentFile in native code.
  2. Der Zugriff überlebt einen Neustart nicht Der Zugriff überlebt einen Neustart nicht takePersistableUriPermission fügen Sie eine kleine native Methode hinzu, wenn Sie das benötigen.
@PluginMethod
public void persist(PluginCall call) {
    Uri uri = Uri.parse(call.getString("uri"));
    getContext().getContentResolver().takePersistableUriPermission(
        uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    call.resolve();
}
  1. Einige Ordner können nicht zugewiesen werden. On Android 11+, verweigert der Picker die Root des internen Speichers, die Root des Ordners und Download und Android/data and Android/obb. Ask users to create or pick a subfolder.

Persisted grants are limited in number per app, and the user can revoke them in system settings or by clearing app data. Check access with getPersistedUriPermissions() Alle Dateien zugreifen: MANAGE_EXTERNAL_STORAGE

Alle Dateien-Zugriff: MANAGE_EXTERNAL_STORAGE

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

This permission gives the old “read and write everything” behavior, but it is not a runtime prompt. The user must enable it on a dedicated settings screen, and Google Play reviews it manually. Accepted use cases are file managers, backup and restore, antivirus, document management, on-device file search, and a few others where broad access is the core feature. A note-taking app, a PDF viewer that opens files from Downloads, or an app that “just needs to save exports” will be rejected.

Wenn Sie außerhalb von Play verteilen (Unternehmens- MDM, kiosk-gestellte Geräte), ist es eine Option. Ansonsten sollten Sie sich auf MediaStore und SAF einstellen.

Wie andere Plugins passen

Plugin Gespeicherte Speicherplatzierung
@capacitor/filesystem Gut mit App-Verzeichnissen. Documents und ExternalStorage wirkt nur für Verzeichnisse auf alten Android-Geräten requestPermissions() nur für die alten Android-Verzeichnisse
@capacitor/camera Verwendet die Systemkamera und den Photo Picker, keine Speicherberechtigung außer saveToGallery auf Android 9 und älter
@capacitor/share . Nur die in der Liste aufgeführten FileProviderkönnen geteilt werden res/xml/file_paths.xml verwendet eigene
@capgo/capacitor-file-sharer und MediaStore für FileProvider keine Konfiguration erforderlich save()Gut mit App-Verzeichnissen.
@capgo/capacitor-downloader Downloads in die App-externe Speicherung, dann verwenden save() um zu exportieren
@capgo/capacitor-file-picker Picker-basiert, keine Berechtigung auf Android 13+
@capgo/capacitor-photo-library Sein Manifest fügt READ_MEDIA_* für die vollständige Bibliothekslayout, unterliegt sie der oben genannten Google-Policy. pickMedia() verwendet das Systempicker und benötigt keine Berechtigung, daher wenn Sie nur das verwenden, entfernen Sie READ_MEDIA_* mit tools:node="remove"

Welche Verzeichnis verwenden

  • Anwendungsdaten, Einstellungen, Datenbanken: Directory.Data
  • Temporäre Dateien und geteilte Exporte: Directory.Cache
  • Große wiederherstellbare Medien: Directory.External auf Android, Directory.LibraryNoCloud On iOS
  • Eine Datei, die der Benutzer in Downloads oder Bilder speichern möchte: MediaStore via FileSharer.save()
  • Eine Datei, die der Benutzer in die App bringt: Picker, dann kopieren Sie sie in Directory.Data
  • Eine Ordner, den der Benutzer möchte, dass die App weiterhin schreibt: SAF Baum URI mit einer persistierten Genehmigung

Für die vollständige Lesen und Schreiben API, siehe die Capacitor-Datei-Handling-Leitfaden.

Ein bestehendes App migrieren

  1. Suchen Sie nach code Directory.Documents, Directory.ExternalStorage, /storage/emulated, und requestLegacyExternalStorage.
  2. Übertragen Sie app-eigene Dateien auf Directory.Data oder Directory.ExternalBeim ersten Start der neuen Version, kopieren Sie alle alten Dateien, die Sie noch lesen können, und löschen Sie sie dann.
  3. Ersetzen Sie die "Speichern auf Gerät"-Funktionen durch FileSharer.save().
  4. Ersetze Pfad-Eingabefelder mit einem Picker.
  5. Hinzufügen android:maxSdkVersion zu Legacy-Speichereinstellungen und entfernen READ_MEDIA_* es sei denn, Sie benötigen es.
  6. Testen Sie auf Geräten oder Emulatoren mit Android 10, 11, 13 und 15 oder 16.

preserveLegacyExternalStorage="true" im <application> tag hält Legacy-Zugriff für Apps bei Updates aus einer Version, die Legacy-Zugriff verwendet requestLegacyExternalStorage, bis die App neu installiert wird. Es hilft nur bei einem einmaligen Migrationsschritt, also sollten Sie nicht auf es zählen.

Schadensbegrenzung

EACCES (Permission denied) Schreiben Sie in /storage/emulated/0/...: Sie schreiben direkt in den gemeinsamen Speicher. Verwenden Sie MediaStore oder einen App-Ordner.

Datei wurde mit Directory.Documents ist nach Neustart verschwunden: Die App verlor die Kontrolle über sie. Verwenden Sie MediaStore für Benutzerdateien.

FileNotFoundException eine URI später content:// URI späterDie temporäre Genehmigung ist abgelaufen. Kopieren Sie das Datei, wenn es ausgewählt wird, oder persistieren Sie die Genehmigung für drei URIs.

Warnung im Google Play Console über Foto- und VideoberechtigungenEin Plugin wurde hinzugefügt READ_MEDIA_IMAGES or READ_MEDIA_VIDEOEntfernen Sie sie mit tools:node="remove" erwähnt, aber schreibt immer noch fehlschlagen

requestPermissions() Zugriffe gewährt, aber Schreibvorgänge fehlschlagen: auf Android 11+ wird die Legacy-Speicherberechtigung erteilt, aber für gemeinsame Speicher ist sie bedeutungslos.

Halten Sie native und Web-Teile im Einklang.

Speicherrichtlinien sind native Konfiguration, daher benötigen Änderungen an der Manifestdatei oder Plugins einen Store-Release, der Capgo Build kann in der Cloud erzeugt werden. Die Dateilogik rund um sie (Pfade, Ordnernamen, Exportflüsse) ist JavaScript, das Sie mit Capgo live Updates.

Live-Updates für Capacitor-Apps

Wenn ein Web-Schicht-Bug live ist, schicken Sie die Reparatur über Capgo anstatt Tage zu warten, bis die App-Store-Zulassung genehmigt ist. Die Benutzer erhalten die Aktualisierung im Hintergrund, während native Änderungen im normalen Review-Verfahren bleiben.

Neuestes von unserem Blog

Capgo bietet Ihnen die besten Einblicke, die Sie benötigen, um eine wirklich professionelle mobile App zu erstellen.