Auf App-spezifische Dateien zugreifen

In vielen Fällen erstellt Ihre Anwendung Dateien, auf die andere Apps nicht zugreifen müssen oder auf die sie keinen Zugriff haben sollen. Das System bietet folgende Speicherorte zum Speichern anwendungsspezifischer Dateien:

  • Interne Speicherverzeichnisse: Diese Verzeichnisse enthalten sowohl einen dedizierten Speicherort zum Speichern persistenter Dateien als auch einen weiteren Speicherort für Cache-Daten. Das System verhindert, dass andere Apps auf diese Standorte zugreifen. Unter Android 10 (API-Level 29) und höher werden diese Standorte verschlüsselt. Aufgrund dieser Eigenschaften eignen sich diese Standorte gut zum Speichern sensibler Daten, auf die nur Ihre Anwendung selbst zugreifen kann.

  • Externe Speicherverzeichnisse: Diese Verzeichnisse enthalten sowohl einen dedizierten Speicherort zum Speichern persistenter Dateien als auch einen weiteren Speicherort für Cache-Daten. Obwohl eine andere Anwendung auf diese Verzeichnisse zugreifen kann, wenn diese Anwendung über die entsprechenden Berechtigungen verfügt, sind die in diesen Verzeichnissen gespeicherten Dateien nur für die Verwendung durch Ihre Anwendung gedacht. Wenn Sie gezielt Dateien erstellen möchten, auf die andere Anwendungen Zugriff haben sollen, sollten Sie diese Dateien stattdessen im freigegebenen Speicher des externen Speichers speichern.

Wenn der Nutzer die App deinstalliert, werden die im app-spezifischen Speicher gespeicherten Dateien entfernt. Daher sollten Sie diesen Speicher nicht verwenden, um Inhalte zu speichern, die der Nutzer unabhängig von Ihrer App erhalten möchte. Wenn Nutzer in Ihrer App beispielsweise Fotos aufnehmen können, würde der Nutzer erwarten, dass er auch nach der Deinstallation der App auf diese Fotos zugreifen kann. Verwenden Sie daher stattdessen freigegebenen Speicher, um diese Dateitypen in der entsprechenden Mediensammlung zu speichern.

In den folgenden Abschnitten wird beschrieben, wie Sie Dateien in anwendungsspezifischen Verzeichnissen speichern und darauf zugreifen.

Zugriff von internem Speicher

Für jede Anwendung stellt das System Verzeichnisse im internen Speicher bereit, in denen eine Anwendung ihre Dateien organisieren kann. Ein Verzeichnis ist für die nichtflüchtigen Dateien Ihrer Anwendung vorgesehen, ein anderes enthält die im Cache gespeicherten Dateien Ihrer Anwendung. Ihre Anwendung benötigt keine Systemberechtigungen, um Dateien in diesen Verzeichnissen zu lesen und zu schreiben.

Andere Apps können nicht auf Dateien im internen Speicher zugreifen. Dadurch wird der interne Speicher zu einem guten Ort für App-Daten, auf die andere Apps nicht zugreifen sollten.

Beachten Sie jedoch, dass diese Verzeichnisse in der Regel klein sind. Bevor Sie appspezifische Dateien in den internen Speicher schreiben, sollte Ihre App den freien Speicherplatz auf dem Gerät abfragen.

Auf persistente Dateien zugreifen

Die gewöhnlichen, persistenten Dateien Ihrer Anwendung befinden sich in einem Verzeichnis, auf das Sie mit dem Attribut filesDir eines Kontextobjekts zugreifen können. Das Framework bietet mehrere Methoden, mit denen Sie auf Dateien in diesem Verzeichnis zugreifen und diese speichern können.

Auf Dateien zugreifen und sie speichern

Mit der File API können Sie auf Dateien zugreifen und diese speichern.

Damit die Leistung Ihrer Anwendung erhalten bleibt, sollten Sie dieselbe Datei nicht mehrmals öffnen und schließen.

Das folgende Code-Snippet zeigt, wie die File API verwendet wird:

Kotlin

val file = File(context.filesDir, filename)

Java

File file = new File(context.getFilesDir(), filename);

Dateien über einen Stream speichern

Alternativ zur File API können Sie auch openFileOutput() aufrufen, um ein FileOutputStream abzurufen, das in eine Datei im filesDir-Verzeichnis schreibt.

Das folgende Code-Snippet zeigt, wie Sie Text in eine Datei schreiben:

Kotlin

val filename = "myfile"
val fileContents = "Hello world!"
context.openFileOutput(filename, Context.MODE_PRIVATE).use {
        it.write(fileContents.toByteArray())
}

Java

String filename = "myfile";
String fileContents = "Hello world!";
try (FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE)) {
    fos.write(fileContents.toByteArray());
}

Damit andere Anwendungen auf Dateien zugreifen können, die in diesem Verzeichnis innerhalb des internen Speichers gespeichert sind, verwenden Sie einen FileProvider mit dem Attribut FLAG_GRANT_READ_URI_PERMISSION.

Über einen Stream auf Dateien zugreifen

Verwenden Sie openFileInput(), um eine Datei als Stream zu lesen:

Kotlin

context.openFileInput(filename).bufferedReader().useLines { lines ->
    lines.fold("") { some, text ->
        "$some\n$text"
    }
}

Java

FileInputStream fis = context.openFileInput(filename);
InputStreamReader inputStreamReader =
        new InputStreamReader(fis, StandardCharsets.UTF_8);
StringBuilder stringBuilder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(inputStreamReader)) {
    String line = reader.readLine();
    while (line != null) {
        stringBuilder.append(line).append('\n');
        line = reader.readLine();
    }
} catch (IOException e) {
    // Error occurred when opening raw file for reading.
} finally {
    String contents = stringBuilder.toString();
}

Liste der Dateien ansehen

Sie können ein Array mit den Namen aller Dateien im Verzeichnis filesDir abrufen. Dazu rufen Sie fileList() auf, wie im folgenden Code-Snippet gezeigt:

Kotlin

var files: Array<String> = context.fileList()

Java

Array<String> files = context.fileList();

Verschachtelte Verzeichnisse erstellen

Sie können auch verschachtelte Verzeichnisse erstellen oder ein inneres Verzeichnis öffnen. Dazu rufen Sie getDir() in Kotlin-basiertem Code auf oder übergeben das Stammverzeichnis und einen neuen Verzeichnisnamen an einen File-Konstruktor in Java-basiertem Code:

Kotlin

context.getDir(dirName, Context.MODE_PRIVATE)

Java

File directory = context.getFilesDir();
File file = new File(directory, filename);

Cache-Dateien erstellen

Wenn Sie sensible Daten nur vorübergehend speichern müssen, sollten Sie zum Speichern der Daten das vorgesehene Cache-Verzeichnis der Anwendung im internen Speicher verwenden. Wie bei allen anwendungsspezifischen Speichern werden die in diesem Verzeichnis gespeicherten Dateien entfernt, wenn der Nutzer die App deinstalliert. Die Dateien in diesem Verzeichnis werden jedoch möglicherweise früher entfernt.

Rufen Sie File.createTempFile() auf, um eine im Cache gespeicherte Datei zu erstellen:

Kotlin

File.createTempFile(filename, null, context.cacheDir)

Java

File.createTempFile(filename, null, context.getCacheDir());

Die Anwendung greift mit dem Attribut cacheDir eines Kontextobjekts und der File API auf eine Datei in diesem Verzeichnis zu:

Kotlin

val cacheFile = File(context.cacheDir, filename)

Java

File cacheFile = new File(context.getCacheDir(), filename);

Cache-Dateien entfernen

Obwohl Android Cache-Dateien manchmal selbst löscht, sollten Sie sich nicht darauf verlassen, dass das System diese Dateien für Sie bereinigt. Sie sollten die Cache-Dateien Ihrer Anwendung immer im internen Speicher aufbewahren.

Mit einer der folgenden Methoden können Sie eine Datei aus dem Cache-Verzeichnis im internen Speicher entfernen:

  • Die Methode delete() für ein File-Objekt, das die Datei darstellt:

    Kotlin

    cacheFile.delete()
    

    Java

    cacheFile.delete();
    
  • Die Methode deleteFile() des App-Kontexts, wobei der Name der Datei übergeben wird:

    Kotlin

    context.deleteFile(cacheFileName)
    

    Java

    context.deleteFile(cacheFileName);
    

Zugriff von externem Speicher

Wenn der interne Speicher nicht genügend Speicherplatz zum Speichern anwendungsspezifischer Dateien bietet, sollten Sie stattdessen einen externen Speicher verwenden. Das System stellt Verzeichnisse innerhalb eines externen Speichers bereit, in denen eine Anwendung Dateien organisieren kann, die dem Nutzer nur innerhalb Ihrer Anwendung einen Mehrwert bieten. Ein Verzeichnis ist für die persistenten Dateien Ihrer Anwendung vorgesehen, ein anderes enthält die im Cache gespeicherten Dateien Ihrer Anwendung.

Unter Android 4.4 (API-Level 19) und höher muss Ihre App keine speicherbezogenen Berechtigungen anfordern, um auf app-spezifische Verzeichnisse innerhalb eines externen Speichers zuzugreifen. Die in diesen Verzeichnissen gespeicherten Dateien werden beim Deinstallieren der Anwendung entfernt.

Auf Geräten mit Android 9 (API-Level 28) oder niedriger kann Ihre App auf App-spezifische Dateien anderer Apps zugreifen, sofern sie die entsprechenden Speicherberechtigungen hat. Apps, die auf Android 10 (API-Level 29) und höher ausgerichtet sind, erhalten standardmäßig eingeschränkten Zugriff auf externen Speicher oder begrenzten Speicher, um Nutzern mehr Kontrolle über ihre Dateien zu geben und das Dateichaos zu reduzieren. Wenn der beschränkte Speicher aktiviert ist, können Anwendungen nicht auf die anwendungsspezifischen Verzeichnisse anderer Anwendungen zugreifen.

Prüfen, ob Speicherplatz verfügbar ist

Da sich der externe Speicher auf einem physischen Volume befindet, das der Nutzer entfernen kann, prüfen Sie, ob das Volume zugänglich ist, bevor Sie versuchen, anwendungsspezifische Daten aus einem externen Speicher zu lesen oder anwendungsspezifische Daten auf einen externen Speicher zu schreiben.

Sie können den Status des Volumes durch Aufrufen von Environment.getExternalStorageState() abfragen. Wenn der zurückgegebene Status MEDIA_MOUNTED ist, können Sie anwendungsspezifische Dateien in den externen Speicher lesen und schreiben. Wenn es MEDIA_MOUNTED_READ_ONLY ist, können Sie diese Dateien nur lesen.

Die folgenden Methoden sind beispielsweise nützlich, um die Speicherverfügbarkeit zu ermitteln:

Kotlin

// Checks if a volume containing external storage is available
// for read and write.
fun isExternalStorageWritable(): Boolean {
    return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
}

// Checks if a volume containing external storage is available to at least read.
fun isExternalStorageReadable(): Boolean {
     return Environment.getExternalStorageState() in
        setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY)
}

Java

// Checks if a volume containing external storage is available
// for read and write.
private boolean isExternalStorageWritable() {
    return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
}

// Checks if a volume containing external storage is available to at least read.
private boolean isExternalStorageReadable() {
     return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ||
            Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
}

Verwenden Sie auf Geräten ohne externen Wechselspeicher den folgenden Befehl, um ein virtuelles Volume zum Testen der Verfügbarkeitslogik für externen Speicher zu aktivieren:

adb shell sm set-virtual-disk true

Physischen Speicherort auswählen

Manchmal stellt ein Gerät, das eine Partition seines internen Speichers als externen Speicher zuweist, auch einen SD-Kartensteckplatz zur Verfügung. Das bedeutet, dass das Gerät mehrere physische Volumes hat, die externen Speicher enthalten könnten. Daher müssen Sie auswählen, welches für Ihren anwendungsspezifischen Speicher verwendet werden soll.

Rufen Sie ContextCompat.getExternalFilesDirs() auf, um auf die verschiedenen Standorte zuzugreifen. Wie im Code-Snippet zu sehen ist, wird das erste Element im zurückgegebenen Array als primäres externes Speicher-Volume betrachtet. Verwenden Sie dieses Volume, es sei denn, es ist voll oder nicht verfügbar.

Kotlin

val externalStorageVolumes: Array<out File> =
        ContextCompat.getExternalFilesDirs(applicationContext, null)
val primaryExternalStorage = externalStorageVolumes[0]

Java

File[] externalStorageVolumes =
        ContextCompat.getExternalFilesDirs(getApplicationContext(), null);
File primaryExternalStorage = externalStorageVolumes[0];

Auf persistente Dateien zugreifen

Rufen Sie getExternalFilesDir() auf, um aus dem externen Speicher auf app-spezifische Dateien zuzugreifen.

Damit die Leistung Ihrer Anwendung erhalten bleibt, sollten Sie dieselbe Datei nicht mehrmals öffnen und schließen.

Das folgende Code-Snippet zeigt, wie getExternalFilesDir() aufgerufen wird:

Kotlin

val appSpecificExternalDir = File(context.getExternalFilesDir(null), filename)

Java

File appSpecificExternalDir = new File(context.getExternalFilesDir(null), filename);

Cache-Dateien erstellen

Wenn Sie dem Cache im externen Speicher eine anwendungsspezifische Datei hinzufügen möchten, rufen Sie einen Verweis auf externalCacheDir ab:

Kotlin

val externalCacheFile = File(context.externalCacheDir, filename)

Java

File externalCacheFile = new File(context.getExternalCacheDir(), filename);

Cache-Dateien entfernen

Wenn Sie eine Datei aus dem externen Cache-Verzeichnis entfernen möchten, verwenden Sie die Methode delete() für ein File-Objekt, das die Datei darstellt:

Kotlin

externalCacheFile.delete()

Java

externalCacheFile.delete();

Medieninhalte

Wenn Ihre Anwendung mit Mediendateien arbeitet, die dem Nutzer nur innerhalb Ihrer Anwendung einen Mehrwert bieten, sollten Sie sie in appspezifischen Verzeichnissen innerhalb eines externen Speichers speichern, wie im folgenden Code-Snippet gezeigt:

Kotlin

fun getAppSpecificAlbumStorageDir(context: Context, albumName: String): File? {
    // Get the pictures directory that's inside the app-specific directory on
    // external storage.
    val file = File(context.getExternalFilesDir(
            Environment.DIRECTORY_PICTURES), albumName)
    if (!file?.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created")
    }
    return file
}

Java

@Nullable
File getAppSpecificAlbumStorageDir(Context context, String albumName) {
    // Get the pictures directory that's inside the app-specific directory on
    // external storage.
    File file = new File(context.getExternalFilesDir(
            Environment.DIRECTORY_PICTURES), albumName);
    if (file == null || !file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

Es ist wichtig, dass Sie Verzeichnisnamen verwenden, die von API-Konstanten wie DIRECTORY_PICTURES bereitgestellt werden. Diese Verzeichnisnamen stellen sicher, dass die Dateien vom System ordnungsgemäß verarbeitet werden. Wenn keiner der vordefinierten Unterverzeichnisnamen zu Ihren Dateien passt, können Sie stattdessen null an getExternalFilesDir() übergeben. Dadurch wird das spezifische Stammverzeichnis der Anwendung im externen Speicher zurückgegeben.

Freien Speicherplatz abfragen

Viele Nutzer haben auf ihren Geräten nicht viel Speicherplatz, daher sollte Ihre App diesen gut nutzen.

Wenn Sie vorab wissen, wie viele Daten Sie speichern, können Sie getAllocatableBytes() aufrufen, um herauszufinden, wie viel Speicherplatz das Gerät Ihrer App zur Verfügung stellen kann. Der Rückgabewert von getAllocatableBytes() kann größer sein als der aktuell kostenlose Speicherplatz auf dem Gerät. Das liegt daran, dass das System Dateien identifiziert hat, die aus den Cache-Verzeichnissen anderer Anwendungen entfernt werden können.

Wenn genügend Speicherplatz zum Speichern der App-Daten vorhanden ist, rufen Sie allocateBytes() auf. Andernfalls kann die App den Nutzer auffordern, einige Dateien vom Gerät zu entfernen oder alle Cache-Dateien vom Gerät zu entfernen.

Das folgende Code-Snippet zeigt ein Beispiel dafür, wie Ihre App freien Speicherplatz auf dem Gerät abfragen kann:

Kotlin

// App needs 10 MB within internal storage.
const val NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L;

val storageManager = applicationContext.getSystemService<StorageManager>()!!
val appSpecificInternalDirUuid: UUID = storageManager.getUuidForPath(filesDir)
val availableBytes: Long =
        storageManager.getAllocatableBytes(appSpecificInternalDirUuid)
if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) {
    storageManager.allocateBytes(
        appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP)
} else {
    val storageIntent = Intent().apply {
        // To request that the user remove all app cache files instead, set
        // "action" to ACTION_CLEAR_APP_CACHE.
        action = ACTION_MANAGE_STORAGE
    }
}

Java

// App needs 10 MB within internal storage.
private static final long NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L;

StorageManager storageManager =
        getApplicationContext().getSystemService(StorageManager.class);
UUID appSpecificInternalDirUuid = storageManager.getUuidForPath(getFilesDir());
long availableBytes =
        storageManager.getAllocatableBytes(appSpecificInternalDirUuid);
if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) {
    storageManager.allocateBytes(
            appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP);
} else {
    // To request that the user remove all app cache files instead, set
    // "action" to ACTION_CLEAR_APP_CACHE.
    Intent storageIntent = new Intent();
    storageIntent.setAction(ACTION_MANAGE_STORAGE);
}

Aktivität zur Speicherverwaltung erstellen

Deine App kann eine benutzerdefinierte Aktivität deklarieren und erstellen, mit der der Nutzer beim Start die Daten verwalten kann, die deine App auf dem Gerät des Nutzers gespeichert hat. Sie deklarieren diese benutzerdefinierte Aktivität „Speicherplatz verwalten“ in der Manifestdatei mit dem Attribut android:manageSpaceActivity. Dateimanager-Apps können diese Aktivität aufrufen, auch wenn die Aktivität nicht von Ihrer App exportiert wird, d. h. wenn android:exported in Ihrer Aktivität auf false festgelegt ist.

Nutzer bitten, einige Dateien auf dem Gerät zu entfernen

Um den Nutzer aufzufordern, auf dem Gerät Dateien auszuwählen, die entfernt werden sollen, rufen Sie einen Intent auf, der die Aktion ACTION_MANAGE_STORAGE enthält. Dieser Intent zeigt dem Nutzer eine Aufforderung an. Bei Bedarf kann in dieser Meldung angezeigt werden, wie viel freier Speicherplatz auf dem Gerät verfügbar ist. Um diese nutzerfreundlichen Informationen anzuzeigen, verwenden Sie das Ergebnis der folgenden Berechnung:

StorageStatsManager.getFreeBytes() / StorageStatsManager.getTotalBytes()

Nutzer bitten, alle Cache-Dateien zu entfernen

Alternativ können Sie den Nutzer bitten, die Cache-Dateien aus allen Apps auf dem Gerät zu löschen. Rufen Sie dazu einen Intent auf, der die Intent-Aktion ACTION_CLEAR_APP_CACHE enthält.

Weitere Informationen

Weitere Informationen zum Speichern von Dateien im Gerätespeicher findest du in den folgenden Ressourcen.

Videos