Externer Speicher

Externer Speicher bezieht sich auf Dateispeicher, der sich nicht im internen Speicher befindet und nicht ausschließlich für die App zugänglich ist, die für die Datei zuständig 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.

In der Vergangenheit bezog sich „externer Speicher“ auf eine Datenträgerpartition auf Wechselmedien, wie etwa eine SD-Karte (auch als tragbarer Speicher bezeichnet). Diese Unterscheidung ist nicht mehr in gleichem Maß relevant, da Android-Geräte sich weiterentwickelt haben und viele Android-Geräte keine Wechselmedien mehr unterstützen. Stattdessen weisen einige Geräte einen Teil ihres internen nicht flüchtigen Arbeitsspeichers Android zu, der die gleiche Funktion wie Wechselmedien ausführen kann. Dies wird als emulierter Speicher bezeichnet und trotzdem als externer Speicher betrachtet. Alternativ können einige Android-Geräte mehrere externe Speicherpartitionen aufweisen. Beispielsweise kann ein Android-Tablet (zusätzlich zum internen Speicher) über emulierten Speicher und einen oder mehrere Slots für eine SD-Karte verfügen. Alle diese Partitionen werden von Android als externer Speicher behandelt.

Auf Geräten mit mehreren Benutzern erhält jeder Benutzer ein dediziertes Verzeichnis auf der primären externen Speicherpartition für den eigenen externen Speicher. Apps, die mit einem Benutzerkonto ausgeführt werden, haben keinen Zugriff auf Dateien eines anderen Benutzers auf dem Gerät. Dateien für alle Benutzer sind trotzdem global lesbar und schreibbar, jedoch schirmt Android jedes Benutzerprofil mithilfe einer Sandbox von den anderen ab.

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. Stattdessen macht Xamarin.Android die nativen Android-APIs verfügbar, die die Bestimmung des Pfads zu Dateien in internem und externem Speicher unterstützen.

In diesem Leitfaden werden die Konzepte und APIs in Android erläutert, die für externen Speicher spezifisch sind.

Öffentliche und private Dateien in externem Speicher

Es gibt zwei verschiedene Typen von Dateien, die eine App im externen Speicher aufbewahren kann:

  • Private Dateien : Private Dateien sind Dateien, die für Ihre Anwendung spezifisch sind (aber immer noch weltweit lesbar und weltweit beschreibbar sind). Android erwartet, dass private Dateien im externen Speicher in einem bestimmten Verzeichnis gespeichert werden. Obwohl die Dateien als „privat“ bezeichnet werden, sind sie trotzdem sichtbar, und andere Apps auf dem Gerät können auf sie zugreifen, sie unterliegen keinem besonderen Schutz durch Android.

  • Öffentliche Dateien: Dies sind Dateien, die nicht als anwendungsspezifisch gelten und für die freie Freigabe bestimmt sind.

Die Unterschiede zwischen diesen Dateien sind in erster Linie konzeptionell. Private Dateien sind insofern privat, als sie als Teil der Anwendung betrachtet werden, während öffentliche Dateien beliebige andere Dateien sind, die in externem Speicher vorhanden sind. Android bietet zwei verschiedene APIs zum Auflösen der Pfade von privaten und öffentlichen Dateien, davon abgesehen werden aber die gleichen .NET-APIs zum Lesen und Schreiben der Dateien verwendet. Dies sind die gleichen APIs, die im Abschnitt zum Lesen und Schreiben erörtert werden.

Private externe Dateien

Private externe Dateien werden als anwendungsspezifisch angesehen (ähnlich wie interne Dateien), werden aber aus beliebigen Gründen in externem Speicher aufbewahrt (etwa, weil sie für den internen Speicher zu groß sind). Ähnlich wie interne Dateien werden diese Dateien gelöscht, wenn die App vom Benutzer deinstalliert wird.

Der primäre Speicherort für private externe Dateien lässt sich durch Aufrufen der Methode Android.Content.Context.GetExternalFilesDir(string type) ermitteln. Diese Methode gibt ein Java.IO.File-Objekt zurück, das das private externe Speicherverzeichnis für die App darstellt. Durch Übergeben von null an diese Methode wird der Pfad zum Speicherverzeichnis des Benutzers für die Anwendung zurückgegeben. Beispielsweise wäre für eine Anwendung mit dem Paketnamen com.companyname.app das „Stamm“-Verzeichnis für private externe Dateien:

/storage/emulated/0/Android/data/com.companyname.app/files/

In diesem Dokument wird auf das Speicherverzeichnis für private Dateien im externen Speicher als PRIVATE_EXTERNAL_STORAGE verwiesen.

Der Parameter für GetExternalFilesDir() ist eine Zeichenfolge, die ein Anwendungsverzeichnis angibt. Dies ist ein Verzeichnis, das dazu bestimmt ist, einen Standardspeicherort für eine logische Dateistruktur bereitzustellen. Die Zeichenfolgenwerte sind in Form von Konstanten in der Android.OS.Environment-Klasse verfügbar:

Android.OS.Environment Verzeichnis
DirectoryAlarms PRIVATE_EXTERNAL_STORAGE/Alarme
DirectoryDcim PRIVATE_EXTERNAL_STORAGE/DCIM
DirectoryDownloads PRIVATE_EXTERNAL_STORAGE/Download
DirectoryDocuments PRIVATE_EXTERNAL_STORAGE/Dokumente
DirectoryMovies PRIVATE_EXTERNAL_STORAGE/Filme
DirectoryMusic PRIVATE_EXTERNAL_STORAGE/Musik
DirectoryNotifications PRIVATE_EXTERNAL_STORAGE/Benachrichtigungen
DirectoryPodcasts PRIVATE_EXTERNAL_STORAGE/Podcasts
DirectoryRingtones PRIVATE_EXTERNAL_STORAGE/Klingeltöne
DirectoryPictures PRIVATE_EXTERNAL_STORAGE/Bilder

Bei Geräten mit mehreren externen Speicherpartitionen weist jede Partition ein Verzeichnis auf, das für private Dateien vorgesehen ist. Die Methode Android.Content.Context.GetExternalFilesDirs(string type) gibt ein Array von Java.IO.Files zurück. Jedes-Objekt stellt ein privates anwendungsspezifisches Verzeichnis auf allen freigegebenen/externen Speichergeräten dar, auf denen die Anwendung die Dateien ablegen kann, die sie besitzt.

Wichtig

Der genaue Pfad zum privaten externen Speicherverzeichnis kann von Gerät zu Gerät und zwischen Android-Versionen abweichen. Aus diesem Grund dürfen Apps den Pfad zu diesem Verzeichnis nicht hart codieren und müssen stattdessen die Xamarin.Android-APIs verwenden, wie etwa Android.Content.Context.GetExternalFilesDir().

Öffentliche externe Dateien

Öffentliche Dateien sind Dateien, die in externem Speicher vorhanden sind und nicht in dem Verzeichnis gespeichert sind, das von Android für private Dateien zugewiesen wird. Öffentliche Dateien werden nicht gelöscht, wenn die App deinstalliert wird. Android-Apps muss zum Lesen oder Schreiben jeglicher öffentlicher Dateien eine Berechtigung erteilt werden. Öffentliche Dateien können an beliebiger Stelle im externen Speicher vorhanden sein, aber gemäß Konvention erwartet Android, dass öffentliche Dateien in dem durch die-Eigenschaft Android.OS.Environment.ExternalStorageDirectorybezeichneten Verzeichnis vorliegen. Diese Eigenschaft gibt ein Java.IO.File-Objekt zurück, das das primäre externe Speicherverzeichnis darstellt. Beispielsweise kann Android.OS.Environment.ExternalStorageDirectory auf das folgende Verzeichnis verweisen:

/storage/emulated/0/

In diesem Dokument wird auf das Speicherverzeichnis für öffentliche Dateien im externen Speicher als PUBLIC_EXTERNAL_STORAGE verwiesen.

Android unterstützt auch das Konzept von Anwendungsverzeichnissen auf PUBLIC_EXTERNAL_STORAGE. Diese Verzeichnisse stimmen exakt mit den Anwendungsverzeichnissen für PRIVATE_EXTERNAL_STORAGE überein und werden in der Tabelle im vorherigen Abschnitt beschrieben. Die-Methode Android.OS.Environment.GetExternalStoragePublicDirectory(string directoryType) gibt ein Java.IO.File-Objekt zurück, das einem öffentlichen Anwendungsverzeichnis entspricht. Der Parameter directoryType ist erforderlich und kann nicht null sein.

Wenn Sie beispielsweise Environment.GetExternalStoragePublicDirectory(Environment.DirectoryDocuments).AbsolutePath aufrufen, wird eine Zeichenfolge ähnlich der folgenden zurückgegeben:

/storage/emulated/0/Documents

Wichtig

Der genaue Pfad zum öffentlichen externen Speicherverzeichnis kann von Gerät zu Gerät und zwischen Android-Versionen abweichen. Aus diesem Grund dürfen Apps den Pfad zu diesem Verzeichnis nicht hart codieren und müssen stattdessen die Xamarin.Android-APIs verwenden, wie etwa Android.OS.Environment.ExternalStorageDirectory.

Arbeiten mit externem Speicher

Nachdem eine Xamarin.Android-App den vollständigen Pfad einer Datei abgerufen hat, sollte sie eine der standardmäßigen .NET-APIs zum Erstellen, Lesen, Schreiben oder Löschen von Dateien nutzen. Dadurch wird die Menge des plattformübergreifend kompatiblen Codes für eine App maximiert. Vor dem Zugriffsversuch auf eine Datei muss eine Xamarin.Android-App jedoch sicherstellen, dass der Zugriff auf die betreffende Datei möglich ist.

  1. Überprüfen des externen Speichers : Je nach Art des externen Speichers ist es möglich, dass er von der App nicht eingebunden und verwendet werden kann. Alle Apps sollten den Status des externen Speichers überprüfen, bevor sie versuchen, ihn zu verwenden.
  2. Ausführen einer Laufzeitberechtigungsprüfung : Eine Android-App muss vom Benutzer eine Berechtigung anfordern, um auf externen Speicher zuzugreifen. Das bedeutet, dass vor jeglichem Dateizugriff eine Berechtigungsanforderung zur Laufzeit erfolgen sollte. Der Leitfaden Berechtigungen in Xamarin.Android enthält weitere Details zu Android-Berechtigungen.

Beide Aufgaben werden unten erörtert.

Überprüfung, ob der externe Speicher verfügbar ist

Der erste Schritt vor dem Schreiben in externen Speicher besteht darin, zu prüfen, ob er lesbar oder beschreibbar ist. Die Android.OS.Environment.ExternalStorageState-Eigenschaft enthält eine Zeichenfolge, die den Status des externen Speichers kennzeichnet. Diese Eigenschaft gibt eine Zeichenfolge zurück, die den Status darstellt. Diese Tabelle stellt eine Liste der Werte von ExternalStorageState dar, die von Environment.ExternalStorageState zurückgegeben werden können:

ExternalStorageState Beschreibung
MediaBadRemoval Das Medium wurde abrupt entfernt, ohne ordnungsgemäße Aufhebung seiner Einbindung.
MediaChecking Das Medium ist vorhanden, wird aber einer Datenträgerprüfung unterzogen.
MediaEjecting Das Medium befindet sich in der Aufhebung der Einbindung und wird dann ausgeworfen.
MediaMounted Das Medium ist eingebunden und kann gelesen oder beschrieben werden.
MediaMountedReadOnly Das Medium ist eingebunden, kann aber nur gelesen werden.
MediaNofs Das Medium ist vorhanden, enthält aber kein für Android geeignetes Dateisystem.
MediaRemoved Es ist kein Medium vorhanden.
MediaShared Das Medium ist vorhanden, aber nicht eingebunden. Es wird per USB mit einem anderen Gerät geteilt.
MediaUnknown Der Status des Mediums wird von Android nicht erkannt.
MediaUnmountable Das Medium ist vorhanden, kann aber von Android nicht eingebunden werden.
MediaUnmounted Das Medium ist vorhanden, aber nicht eingebunden.

Die meisten Android-Apps brauchen lediglich zu überprüfen, ob externer Speicher eingebunden ist. Der folgende Codeausschnitt zeigt, wie überprüft werden kann, ob der externe Speicher nur für Lesezugriff oder für Schreib-Lesezugriff eingebunden ist:

bool isReadonly = Environment.MediaMountedReadOnly.Equals(Environment.ExternalStorageState);
bool isWriteable = Environment.MediaMounted.Equals(Environment.ExternalStorageState);

Berechtigungen für externen Speicher

Android betrachtet den Zugriff auf externen Speicher als gefährliche Berechtigung, was es normalerweise erforderlich macht, dass der Benutzer die Berechtigung zum Zugriff auf die Ressource erteilt. Der Benutzer kann diese Berechtigung jederzeit widerrufen. Das bedeutet, dass vor jeglichem Dateizugriff eine Berechtigungsanforderung zur Laufzeit erfolgen sollte. Apps werden automatisch Berechtigungen zum Lesen und Schreiben ihrer eigenen privaten Dateien erteilt. Apps können die privaten Dateien anderer Apps lesen und schreiben, nachdem ihnen vom Benutzer die Berechtigung erteilt wurde.

Alle Android-Apps müssen eine der zwei Berechtigungen für externen Speicher im AndroidManifest.xml deklarieren. Zum Bestimmen der Berechtigungen muss eines der folgenden zwei uses-permission-Elemente zu AndroidManifest.xml hinzugefügt werden:

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

Hinweis

Wenn der Benutzer WRITE_EXTERNAL_STORAGE erteilt, ist READ_EXTERNAL_STORAGE implizit ebenfalls erteilt. Es ist nicht erforderlich, in AndroidManifest.xml beide Berechtigungen anzufordern.

Die Berechtigungen können ebenfalls mithilfe der Registerkarte Android Manifest der Projektmappeneigenschaften hinzugefügt werden:

Projektmappen-Explorer: Erforderliche Berechtigungen für Visual Studio

Generell gilt, dass alle gefährlichen Berechtigungen vom Benutzer genehmigt werden müssen. Die Berechtigungen für externen Speicher stellen insofern eine Anomalie dar, als dass es Ausnahmen von dieser Regel gibt, je nach der Android-Version, unter der die App ausgeführt wird:

Flussdiagramm der Externen Speicherberechtigungsprüfungen

Weitere Informationen zum Ausführen von Berechtigungsanforderungen zur Laufzeit finden Sie im Leitfaden Berechtigungen in Xamarin.Android. Das Monodroid-BeispielLocalFiles zeigt auch eine Möglichkeit, Laufzeitberechtigungsprüfungen durchzuführen.

Erteilen und Widerrufen von Berechtigungen mithilfe von ADB

Im Lauf der Entwicklung einer Android-App kann es erforderlich sein, Berechtigungen zu erteilen und zu widerrufen, um die verschiedenen Workflows zu testen, die mit Berechtigungsprüfungen zur Laufzeit einhergehen. Dies kann an der Eingabeaufforderung mithilfe von ADB erfolgen. Die folgenden Befehlszeilenausschnitte veranschaulichen, wie Berechtigungen mithilfe von ADB für eine Android-App erteilt oder widerrufen werden, deren Paketname com.companyname.app ist:

$ adb shell pm grant com.companyname.app android.permission.WRITE_EXTERNAL_STORAGE

$ adb shell pm revoke com.companyname.app android.permission.WRITE_EXTERNAL_STORAGE

Löschen von Dateien

Zum Löschen einer Datei aus dem externen Speicher kann jede der standardmäßigen C#-APIs verwendet werden, beispielsweise System.IO.File.Delete. Es ist ferner möglich, die Java-APIs zu verwenden, um den Preis der Portierbarkeit des Codes. Zum Beispiel:

System.IO.File.Delete("/storage/emulated/0/Android/data/com.companyname.app/files/count.txt");