Auf App-spezifische Dateien zugreifen

In vielen Fällen erstellt Ihre App Dateien, auf die andere Apps nicht zugreifen müssen oder sollten. Das System bietet die folgenden Speicherorte für solche app-spezifischen Dateien:

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

  • Externe Speicherverzeichnisse: Diese Verzeichnisse enthalten sowohl einen speziellen Speicherort für das Speichern nichtflüchtiger Dateien als auch einen anderen Speicherort für das Speichern von Cache-Daten. Andere Apps können zwar auf diese Verzeichnisse zugreifen, wenn sie die entsprechenden Berechtigungen haben, die darin gespeicherten Dateien sind jedoch nur für Ihre App bestimmt. Wenn Sie Dateien erstellen möchten, auf die andere Apps zugreifen können sollen, sollten Sie diese stattdessen im freigegebenen Speicher des externen Speichers ablegen.

Wenn der Nutzer Ihre App deinstalliert, werden die im app-spezifischen Speicher gespeicherten Dateien entfernt. Aus diesem Grund sollten Sie diesen Speicher nicht verwenden, um etwas zu speichern, das der Nutzer voraussichtlich unabhängig von Ihrer App aufbewahrt. Wenn Nutzer beispielsweise in Ihrer App Fotos aufnehmen können, erwarten sie, dass sie auch nach der Deinstallation der App auf diese Fotos zugreifen können. Sie sollten daher stattdessen freigegebenen Speicher verwenden, um diese Dateitypen in der entsprechenden Mediensammlung zu speichern.

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

Zugriff von internem Speicher

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

Andere Apps können nicht auf im internen Speicher gespeicherte Dateien zugreifen. Dies macht den internen Speicher zu einem guten Ort für App-Daten, auf die andere Apps nicht zugreifen sollten.

Diese Verzeichnisse sind jedoch in der Regel klein. Bevor appspezifische Dateien in den internen Speicher geschrieben werden, sollte die Anwendung den freien Speicherplatz auf dem Gerät abfragen.

Auf persistente Dateien zugreifen

Die gewöhnlichen, nichtflüchtigen 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 sie dort speichern können.

Auf Dateien zugreifen und sie speichern

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

Öffnen und schließen Sie dieselbe Datei nicht mehrmals, um die Leistung Ihrer App zu erhalten.

Im folgenden Code-Snippet wird die Verwendung der File API veranschaulicht:

Kotlin

val file = File(context.filesDir, filename)

Java

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

Dateien mithilfe eines Streams speichern

Als Alternative zur Verwendung der File API können Sie openFileOutput() aufrufen, um eine FileOutputStream abzurufen, die in eine Datei im Verzeichnis filesDir schreibt.

Das folgende Code-Snippet zeigt, wie Text in eine Datei geschrieben wird:

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());
}

Wenn Sie anderen Apps den Zugriff auf Dateien erlauben, die in diesem Verzeichnis im internen Speicher gespeichert sind, verwenden Sie ein FileProvider mit dem Attribut FLAG_GRANT_READ_URI_PERMISSION.

Über einen Stream auf eine Datei 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 aufrufen

Sie können ein Array mit den Namen aller Dateien im Verzeichnis filesDir abrufen, indem Sie fileList() aufrufen, 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, indem Sie getDir() in Kotlin-basiertem Code aufrufen oder das Stammverzeichnis und einen neuen Verzeichnisnamen in einen File-Konstruktor in Java-basierten Code übergeben:

Kotlin

context.getDir(dirName, Context.MODE_PRIVATE)

Java

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

Cachedateien erstellen

Wenn Sie vertrauliche Daten nur vorübergehend speichern müssen, sollten Sie das Cache-Verzeichnis der App im internen Speicher verwenden, um die Daten zu speichern. Wie bei allen App-spezifischen 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 zum Erstellen einer im Cache gespeicherten Datei File.createTempFile() auf:

Kotlin

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

Java

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

Ihre 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

Auch wenn Android manchmal Cachedateien automatisch löscht, sollten Sie nicht darauf vertrauen, dass das System diese Dateien für Sie bereinigt. Sie sollten die Cache-Dateien Ihrer Anwendung immer im internen Speicher belassen.

So entfernen Sie eine Datei aus dem Cacheverzeichnis im internen Speicher:

  • Die Methode delete() auf einem File-Objekt, das die Datei darstellt:

    Kotlin

    cacheFile.delete()
    

    Java

    cacheFile.delete();
    
  • Die Methode deleteFile() des Kontexts der App, 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 des 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 nichtflüchtigen Dateien Ihrer Anwendung vorgesehen und ein anderes enthält die im Cache gespeicherten Dateien Ihrer Anwendung.

Unter Android 4.4 (API-Level 19) oder höher muss deine App keine speicherbezogenen Berechtigungen anfordern, um auf appspezifische Verzeichnisse innerhalb des externen Speichers zuzugreifen. Die in diesen Verzeichnissen gespeicherten Dateien werden entfernt, wenn die Anwendung deinstalliert wird.

Auf Geräten mit Android 9 (API-Level 28) oder niedriger kann deine App auf die App-spezifischen Dateien anderer Apps zugreifen, sofern deine App die entsprechenden Speicherberechtigungen hat. Damit Nutzer mehr Kontrolle über ihre Dateien haben und die Anzahl der Dateien reduziert wird, erhalten Apps, die auf Android 10 (API-Level 29) oder höher ausgerichtet sind, standardmäßig eingeschränkten Zugriff auf den externen Speicher, also auf eingeschränkten Speicher. Wenn begrenzter Speicher aktiviert ist, können Anwendungen nicht auf die anwendungsspezifischen Verzeichnisse zugreifen, die zu anderen Anwendungen gehören.

Prüfen, ob Speicherplatz verfügbar ist

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

Sie können den Status des Lautstärkereglers abfragen, indem Sie Environment.getExternalStorageState() aufrufen. Wenn der zurückgegebene Status MEDIA_MOUNTED lautet, können Sie anwendungsspezifische Dateien im 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);
}

Auf Geräten ohne externen Wechselspeicher können Sie mit dem folgenden Befehl ein virtuelles Volume aktivieren, um die Logik zur Verfügbarkeit des externen Speichers zu testen:

adb shell sm set-virtual-disk true

Speicherort für physische Daten auswählen

Manchmal haben Geräte, die eine Partition ihres internen Speichers als externen Speicher zuweisen, auch einen SD-Kartensteckplatz. Das bedeutet, dass das Gerät über mehrere physische Volumes verfügt, die externen Speicher enthalten können. Daher müssen Sie auswählen, welches für Ihren anwendungsspezifischen Speicher verwendet werden soll.

Wenn Sie auf die verschiedenen Standorte zugreifen möchten, rufen Sie ContextCompat.getExternalFilesDirs() auf. Wie im Code-Snippet gezeigt, 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

Wenn Sie auf app-spezifische Dateien aus dem externen Speicher zugreifen möchten, rufen Sie getExternalFilesDir() auf.

Öffnen und schließen Sie dieselbe Datei nicht mehrmals, um die Leistung Ihrer App zu erhalten.

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 app-spezifische Datei hinzufügen möchten, rufen Sie eine Referenz auf die externalCacheDir ab:

Kotlin

val externalCacheFile = File(context.externalCacheDir, filename)

Java

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

Cache-Dateien entfernen

Wenn du eine Datei aus dem externen Cache-Verzeichnis entfernen möchtest, verwende die Methode delete() auf ein File-Objekt, das die Datei darstellt:

Kotlin

externalCacheFile.delete()

Java

externalCacheFile.delete();

Medieninhalte

Wenn Ihre App mit Mediendateien arbeitet, die dem Nutzer nur innerhalb Ihrer App einen Mehrwert bieten, empfiehlt es sich, sie in anwendungsspezifischen Verzeichnissen innerhalb des externen Speichers zu 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 sorgen dafür, dass die Dateien vom System richtig behandelt werden. Wenn keiner der vordefinierten Unterverzeichnisnamen für Ihre Dateien geeignet ist, können Sie stattdessen null an getExternalFilesDir() übergeben. Dadurch wird das anwendungsspezifische Stammverzeichnis innerhalb des externen Speichers zurückgegeben.

Freien Speicherplatz abfragen

Viele Nutzer haben nicht viel Speicherplatz auf ihren Geräten. Ihre Anwendung sollte daher Speicherplatz mit Bedacht belegen.

Wenn Sie im Voraus wissen, wie viele Daten Sie speichern, können Sie herausfinden, wie viel Speicherplatz das Gerät Ihrer App zur Verfügung stellen kann, indem Sie getAllocatableBytes() aufrufen. Der Rückgabewert von getAllocatableBytes() ist möglicherweise größer als die aktuelle Menge an freiem Speicherplatz auf dem Gerät. Das liegt daran, dass das System Dateien erkannt hat, die aus den Cache-Verzeichnissen anderer Apps entfernt werden können.

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

Das folgende Code-Snippet zeigt ein Beispiel dafür, wie Ihre App den 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

Ihre App kann eine benutzerdefinierte Aktivität deklarieren und erstellen, mit der der Nutzer nach dem Starten die Daten verwalten kann, die Ihre App auf seinem Gerät gespeichert hat. Sie deklarieren diese benutzerdefinierte Aktivität „Raum verwalten“ mit dem Attribut android:manageSpaceActivity in der Manifestdatei. Dateimanager-Apps können diese Aktivität aufrufen, auch wenn Ihre App die Aktivität nicht exportiert, d. h. wenn Ihre Aktivität android:exported auf false festlegt.

Nutzer bitten, einige Gerätedateien zu entfernen

Damit der Nutzer Dateien auf dem Gerät auswählen soll, die entfernt werden sollen, rufen Sie einen Intent auf, der die Aktion ACTION_MANAGE_STORAGE enthält. Mit diesem Intent wird dem Nutzer eine Aufforderung angezeigt. Auf Wunsch kann dieser Prompt den verfügbaren freien Speicherplatz auf dem Gerät anzeigen. Verwenden Sie das Ergebnis der folgenden Berechnung, um diese nutzerfreundlichen Informationen anzuzeigen:

StorageStatsManager.getFreeBytes() / StorageStatsManager.getTotalBytes()

Nutzer bitten, alle Cache-Dateien zu entfernen

Alternativ kannst du den Nutzer bitten, die Cachedateien aller 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 finden Sie in den folgenden Ressourcen.

Videos