Dateispeicherung und Dateizugriff mit Xamarin.Android

Eine häufige Anforderung für Android-Apps ist das Bearbeiten von Dateien – Speichern von Bildern, Herunterladen von Dokumenten oder Exportieren von Daten für die Freigabe für andere Programme. Das (auf Linux basierende) Android unterstützt diese Funktionalität durch die Bereitstellung von Speicherplatz für den Dateispeicher. Android unterteilt das Dateisystem in zwei Arten von Speicher:

  • Interner Speicher : Dies ist ein Teil des Dateisystems, auf den nur die Anwendung oder das Betriebssystem zugreifen kann.
  • Externer Speicher : Dies ist eine Partition zum Speichern von Dateien, auf die alle Apps, der Benutzer und möglicherweise andere Geräte zugreifen können. Auf einigen Geräten kann es sich bei dem externen Speicher um einen Wechseldatenträger handeln (z. B. eine SD-Karte).

Dies ist eine konzeptionelle Unterteilung, sie bezieht sich nicht unbedingt auf eine einzelne Partition oder ein einzelnes Verzeichnis auf dem Gerät. Ein Android-Gerät stellt immer eine Partition für den internen und den externen Speicher bereit. Es ist möglich, dass bestimmte Geräte über mehrere Partitionen verfügen, die als externer Speicher betrachtet werden. Unabhängig von der Partition sind die APIs zum Lesen, Schreiben oder Erstellen von Dateien dieselben. Es gibt zwei API-Sätze, die eine Xamarin.Android-Anwendung für den Dateizugriff nutzen kann:

  1. Die .NET-APIs (bereitgestellt von Mono und umschlossen von Xamarin.Android): Dazu gehören die Dateisystemhilfsprogramme , die von Xamarin.Essentials bereitgestellt werden. Die .NET-APIs bieten die beste plattformübergreifende Kompatibilität und stehen in diesem Leitfaden deshalb im Vordergrund.
  2. Die nativen Java-Dateizugriffs-APIs (von Java bereitgestellt und von Xamarin.Android umschlossen) – Java stellt eigene APIs zum Lesen und Schreiben von Dateien bereit. Diese APIs sind eine akzeptable Alternative zu den .NET-APIs, jedoch spezifisch für Android und nicht für Anwendungen geeignet, die plattformübergreifend eingesetzt werden sollen.

Das Lesen aus und Schreiben in Dateien erfolgt in Xamarin.Android nahezu identisch wie in jeder anderen .NET-Anwendung. Die Xamarin.Android-App bestimmt den Pfad zur Datei, die bearbeitet werden soll, und verwendet anschließend .NET-Standardausdrücke für den Dateizugriff. Da sich die tatsächlichen Pfade zu externem und internem Speicher von Gerät zu Gerät oder von Android-Version zu Android-Version unterscheiden können, empfiehlt es sich nicht, die Dateipfade hart zu codieren. Verwenden Sie stattdessen die Xamarin.Android-APIs, um den Pfad zu den Dateien zu ermitteln. Hierbei machen die .NET-APIs zum Lesen und Schreiben von Dateien die nativen Android-APIs verfügbar, um den Pfad zu Dateien im internen oder externen Speicher zu bestimmen.

Vor einer Erläuterung der APIs für den Dateizugriff ist es wichtig, einige Details im Zusammenhang mit dem internen und externen Speicher zu verstehen. Diese werden im nächsten Abschnitt erläutert.

Interner und externer Speicher im Vergleich

Vom Konzept her sind interner Speicher und externer Speicher sehr ähnlich – beides sind Orte, an denen eine Xamarin.Android-App Dateien speichern kann. Diese Ähnlichkeit kann für Entwickler, die mit Android nicht vertraut sind, verwirrend sein, da möglicherweise unklar ist, wann eine Anwendung internen und wann externen Speicher verwenden sollte.

Als interner Speicher wird der permanente Speicher bezeichnet, den Android dem Betriebssystem, APKs und einzelnen Apps zuweist. Auf diesen Speicher kann nur durch das Betriebssystem oder Apps zugegriffen werden. Android weist jeder App ein Verzeichnis in der Partition des internen Speichers zu. Wird die App deinstalliert, werden alle im internen Speicher enthaltenen Dateien in diesem Verzeichnis ebenfalls gelöscht. Der interne Speicher eignet sich besonders für Dateien, auf die nur von der App zugegriffen werden kann und die nicht für andere Apps freigegeben werden oder nach dem Deinstallieren der App nicht mehr relevant sind. Unter Android 6.0 oder höher können Dateien im internen Speicher von Google mithilfe der Funktion zur automatischen Sicherung in Android 6.0 automatisch gesichert werden. Der interne Speicher hat folgende Nachteile:

  • Dateien können nicht freigegeben werden.
  • Dateien werden gelöscht, wenn die App deinstalliert wird.
  • Der im internen Speicher verfügbare Speicherplatz ist möglicherweise begrenzt.

Als externer Speicher wird der Dateispeicher bezeichnet, bei dem es sich nicht um internen Speicher handelt und der nicht ausschließlich für eine App zugänglich ist. Der Hauptzweck von externem Speicher besteht darin, einen Aufbewahrungsort für Dateien bereitzustellen, die gemeinsam von verschiedenen Apps verwendet werden sollen oder zu groß für den internen Speicher sind. Der Vorteil des externen Speichers besteht darin, dass er üblicherweise sehr viel mehr Platz für Dateien bietet als der interne Speicher. Es ist jedoch nicht immer garantiert, dass ein externer Speicher auf einem Gerät vorhanden ist, und möglicherweise werden für den Zugriff auf den externen Speicher spezielle Benutzerberechtigungen benötigt.

Hinweis

Bei Geräten, die mehrere Benutzer unterstützen, stellt Android jedem Benutzer ein eigenes Verzeichnis im internen und im externen Speicher zur Verfügung. Dieses Verzeichnis ist für andere Benutzer auf dem Gerät nicht zugänglich. Diese Trennung ist für Apps nicht sichtbar, solange keine hartcodierten Pfade zu Dateien im internen oder externen Speicher verwendet werden.

Als Faustregel gilt: Dateien von Xamarin.Android-Anwendungen sollten vorzugsweise im internen Speicher abgelegt werden, wenn dies sinnvoll ist. Auf den externen Speicher sollte zurückgegriffen werden, wenn Dateien für andere Apps freigegeben werden müssen, die Dateien sehr groß sind oder auch dann beibehalten werden sollten, wenn die App deinstalliert wird. Beispielsweise ist eine Konfigurationsdatei am besten für einen internen Speicher geeignet, da sie nur für die App wichtig ist, über die sie erstellt wird. Fotos dagegen sind ein guter Kandidat für einen externen Speicher. Sie können sehr groß sein, und in vielen Fällen möchte der Benutzer sie teilen oder weiter darauf zugreifen, auch wenn die App deinstalliert wurde.

Dieser Leitfaden konzentriert sich auf den internen Speicher. Ausführliche Informationen zur Verwendung von externem Speicher in einer Xamarin.Android-Anwendung finden Sie im Leitfaden Externer Speicher.

Arbeiten mit internem Speicher

Das interner Speicherverzeichnis für eine Anwendung wird durch das Betriebssystem bestimmt und durch die Android.Content.Context.FilesDir-Eigenschaft für Android-Apps verfügbar gemacht. So wird ein Java.IO.File-Objekt zurückgegeben, das das Verzeichnis repräsentiert, das Android exklusiv für die App reserviert hat. Beispielsweise kann das interne Speicherverzeichnis für eine App mit dem Paketnamen com.companyname so lauten:

/data/user/0/com.companyname/files

In diesem Dokument wird das interne Speicherverzeichnis als INTERNAL_STORAGE bezeichnet.

Wichtig

Der genaue Pfad zum internen Speicherverzeichnis kann je nach Gerät und Android-Version abweichen. Aus diesem Grund dürfen Apps den Pfad zu diesem Verzeichnis nicht hartcodieren und müssen stattdessen die Xamarin.Android-APIs verwenden, wie z. B. System.Environment.GetFolderPath().

Um die gemeinsame Nutzung von Code zu maximieren, sollten Xamarin.Android-Apps (oder auf Xamarin.Android ausgerichtete Xamarin.Forms-Apps) die System.Environment.GetFolderPath()-Methode verwenden. In Xamarin.Android gibt diese Methode eine Zeichenfolge für ein Verzeichnis zurück, das sich am selben Speicherort befindet wie Android.Content.Context.FilesDir. Diese Methode erwartet eine Enumeration (System.Environment.SpecialFolder). Mit dieser wird ein Satz von Enumerationskonstanten identifiziert, die die Pfade zu speziellen, vom Betriebssystem verwendeten Ordnern darstellen. Nicht alle System.Environment.SpecialFolder-Werte werden einem gültigen Verzeichnis in Xamarin.Android zugeordnet. Die folgende Tabelle beschreibt, welcher Pfad für einen vorhandenen Wert von System.Environment.SpecialFolder erwartet werden kann:

System.Environment.SpecialFolder `Path`
ApplicationData INTERNAL_STORAGE/.config
Desktop INTERNAL_STORAGE/Desktop
LocalApplicationData INTERNAL_STORAGE/.local/share
MyDocuments INTERNAL_STORAGE
MyMusic INTERNAL_STORAGE/Musik
MyPictures INTERNAL_STORAGE/Bilder
MyVideos INTERNAL_STORAGE/Videos
Personal INTERNAL_STORAGE
Fonts INTERNAL_STORAGE/.fonts
Templates INTERNAL_STORAGE/Vorlagen
CommonApplicationData /usr/share
CommonApplicationData /usr/share

Lesen oder Schreiben von Dateien in den internen Speicher

Jede der C#-APIs für Schreibvorgänge in eine Datei ist ausreichend. Es muss lediglich der Pfad zur Datei ermittelt werden, die sich in dem der Anwendung zugewiesenen Verzeichnis befindet. Es wird dringend empfohlen, die asynchronen Versionen der .NET-APIs zu verwenden, um Probleme zu minimieren, die durch eine Blockierung des Hauptthreads durch den Dateizugriff entstehen könnten.

Dieser Codeausschnitt ist ein Beispiel zum Schreiben einer ganzen Zahl in eine UTF-8-Textdatei im internen Speicherverzeichnis einer Anwendung:

public async Task SaveCountAsync(int count)
{
    var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "count.txt");
    using (var writer = File.CreateText(backingFile))
    {
        await writer.WriteLineAsync(count.ToString());
    }
}

Der nächste Codeausschnitt bietet eine Möglichkeit, einen ganzzahligen Wert zu lesen, der in einer Textdatei gespeichert wurde:

public async Task<int> ReadCountAsync()
{
    var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "count.txt");

    if (backingFile == null || !File.Exists(backingFile))
    {
        return 0;
    }

    var count = 0;
    using (var reader = new StreamReader(backingFile, true))
    {
        string line;
        while ((line = await reader.ReadLineAsync()) != null)
        {
            if (int.TryParse(line, out var newcount))
            {
                count = newcount;
            }
        }
    }

    return count;
}

Verwenden von Xamarin.Essentials – Dateisystemhilfsprogramme

Xamarin.Essentials ist ein Satz mit APIs zum Schreiben von plattformübergreifendem, kompatiblem Code. Bei den Dateisystemhilfsprogrammen handelt es sich um eine Klasse mit verschiedenen Hilfsprogrammen zum einfacheren Auffinden der Cache- und Datenverzeichnisse einer Anwendung. Dieser Codeausschnitt bietet ein Beispiel für die Ermittlung des internen Speicherverzeichnisses und des Cacheverzeichnisses für eine App:

// Get the path to a file on internal storage
var backingFile = Path.Combine(Xamarin.Essentials.FileSystem.AppDataDirectory, "count.txt");

// Get the path to a file in the cache directory
var cacheFile = Path.Combine(Xamarin.Essentials.FileSystem.CacheDirectory, "count.txt");

Ausblenden von Dateien im MediaStore

Der MediaStore ist eine Android-Komponente, in der Metadaten zu den Mediendateien (Videos, Musik, Bilder) auf einem Android-Gerät erfasst werden. Der Zweck dieses Speichers besteht darin, die Freigabe dieser Dateien für alle Android-Apps auf dem Gerät zu vereinfachen.

Private Dateien werden nicht als Medien angezeigt, die freigegeben werden können. Wenn eine App beispielsweise ein Bild im privaten externen Speicher der App ablegt, wird diese Datei nicht von der Medienerkennung erfasst (MediaStore).

Öffentliche Dateien werden vom MediaStore erfasst. Verzeichnisse, die eine 0-Byte-Datei namens .NOMEDIA aufweisen, werden von MediaStore nicht erfasst.