Viele Apps bieten Nutzern die Möglichkeit, Medien auf einem externen Speicher zu speichern und darauf zuzugreifen, um die Nutzerfreundlichkeit zu verbessern. Das Framework bietet einen optimierten Index für Mediensammlungen, den sogenannten Medienspeicher. mit der Nutzer diese Mediendateien leichter abrufen und aktualisieren können. Gleichmäßig nachdem die App deinstalliert wurde, bleiben diese Dateien auf dem Gerät des Nutzers.
Bildauswahl
Als Alternative zur Verwendung des Medienspeichers bietet das Bildauswahl-Tool von Android eine sichere, integrierte Möglichkeit für Nutzer, Mediendateien auszuwählen, ohne um Ihrer App Zugriff auf ihre gesamte Mediathek zu gewähren. Diese Option ist nur verfügbar, auf unterstützten Geräten. Weitere Informationen finden Sie in der Leitfaden für die Bildauswahl.
Media-Shop
Um mit der Abstraktion des Media Stores zu interagieren, verwenden Sie einen
ContentResolver
-Objekt, das Sie
aus dem Kontext Ihrer App abrufen:
Kotlin
val projection = arrayOf(media-database-columns-to-retrieve) val selection = sql-where-clause-with-placeholder-variables val selectionArgs = values-of-placeholder-variables val sortOrder = sql-order-by-clause applicationContext.contentResolver.query( MediaStore.media-type.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder )?.use { cursor -> while (cursor.moveToNext()) { // Use an ID column from the projection to get // a URI representing the media item itself. } }
Java
String[] projection = new String[] { media-database-columns-to-retrieve }; String selection = sql-where-clause-with-placeholder-variables; String[] selectionArgs = new String[] { values-of-placeholder-variables }; String sortOrder = sql-order-by-clause; Cursor cursor = getApplicationContext().getContentResolver().query( MediaStore.media-type.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder ); while (cursor.moveToNext()) { // Use an ID column from the projection to get // a URI representing the media item itself. }
Das System scannt ein externes Speicher-Volume automatisch und fügt Mediendateien hinzu klar definierten Sammlungen hinzugefügt:
- Bilder,einschließlich Fotos und Screenshots, die in den
DCIM/
undPictures/
. Das System fügt diese DateienMediaStore.Images
-Tabelle. - Videos, die in
DCIM/
,Movies/
undPictures/
gespeichert werden Verzeichnisse enthalten. Das System fügt diese DateienMediaStore.Video
-Tabelle. - Audiodateien, die in den folgenden Formaten gespeichert sind:
Alarms/
,Audiobooks/
,Music/
Notifications/
,Podcasts/
undRingtones/
. Darüber hinaus enthält der das System Audioplaylists erkennt, die sich inMusic/
oderMovies/
befinden. sowie Sprachaufnahmen in derRecordings/
-Verzeichnis. Das System fügt diese DateienMediaStore.Audio
-Tabelle. Das VerzeichnisRecordings/
ist unter Android 11 (API-Level 30) und niedriger nicht verfügbar. - Heruntergeladene Dateien,die im Verzeichnis
Download/
gespeichert sind. An mit Android 10 (API-Level 29) oder höher verwenden, werden diese Dateien imMediaStore.Downloads
. Diese Tabelle ist für Android 9 (API-Level 28) und niedriger nicht verfügbar.
Der Media Store enthält auch eine Sammlung namens
MediaStore.Files
Inhalt
hängen davon ab, ob Ihre App einen Bereich
Speicherplatz, der in Apps verfügbar ist, die auf
Android 10 oder höher.
- Wenn der beschränkte Speicher aktiviert ist, werden in der Sammlung nur Fotos, Videos,
und Audiodateien, die Ihre App erstellt hat. Die meisten Entwickler müssen nicht
MediaStore.Files
, um Mediendateien aus anderen Apps anzusehen. Wenn du jedoch eine Anforderung zu erfüllen, können Sie dieREAD_EXTERNAL_STORAGE
Berechtigung. Wir empfehlen jedoch, dieMediaStore
zu verwenden APIs zum Öffnen von Dateien, die nicht von deiner Anwendung erstellt wurden. - Wenn begrenzter Speicher nicht verfügbar ist oder nicht verwendet wird, werden in der Sammlung alle verschiedene Arten von Mediendateien.
Erforderliche Berechtigungen anfordern
Bevor Sie Vorgänge auf Mediendateien ausführen, müssen Sie dafür sorgen, dass Ihre App die Berechtigungen für den Zugriff auf diese Dateien hat. Achten Sie jedoch darauf, Berechtigungen deklarieren, die deine App nicht benötigt oder verwendet.
Speicherberechtigungen
Ob deine App Berechtigungen für den Speicherzugriff benötigt, hängt davon ab, ob sie nur eigene Mediendateien oder Dateien, die von anderen Apps erstellt wurden.
Zugriff auf eigene Mediendateien
Auf Geräten mit Android 10 oder höher benötigen Sie
für den Zugriff auf und die Änderung von Mediendateien,
der App gehört, einschließlich Dateien im MediaStore.Downloads
. Wenn Sie zum Beispiel eine Kamera-App entwickeln,
speicherbezogene Berechtigungen anfordern, um auf die aufgenommenen Fotos zuzugreifen.
App ist Inhaber der Bilder, die Sie in den Media Store schreiben.
Auf andere Apps zugreifen Mediendateien
Wenn du auf Mediendateien zugreifen möchtest, die von anderen Apps erstellt werden, musst du die entsprechende Speicherberechtigungen, und die Dateien müssen sich in einem der folgenden Mediensammlungen:
Solange eine Datei über MediaStore.Images
angesehen werden kann,
MediaStore.Video
- oder MediaStore.Audio
-Suchanfragen können Sie sie auch über die
MediaStore.Files
-Abfrage.
Im folgenden Code-Snippet sehen Sie, wie der geeignete Speicher für Berechtigungen:
<!-- Required only if your app needs to access images or photos that other apps created. --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- Required only if your app needs to access videos that other apps created. --> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- Required only if your app needs to access audio files that other apps created. --> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
Für Apps, die auf älteren Geräten ausgeführt werden, sind zusätzliche Berechtigungen erforderlich
Wenn Ihre App auf einem Gerät mit Android 9 oder niedriger verwendet wird oder
Ihre App ist vorübergehend ausgeschlossen.
speichern, müssen Sie
anfordern:
READ_EXTERNAL_STORAGE
Zugriff auf beliebige Mediendateien. Wenn du Mediendateien ändern möchtest, musst du
anfordern:
WRITE_EXTERNAL_STORAGE
um eine Genehmigung zu erhalten.
Storage Access Framework für den Zugriff auf Downloads anderer Apps erforderlich
Wenn Ihre App auf eine Datei in der Sammlung MediaStore.Downloads
zugreifen möchte
die Ihre App nicht erstellt hat, müssen Sie das Storage Access Framework verwenden. Weitere Informationen zur Verwendung dieses Frameworks finden Sie unter Auf Dokumente und andere Dateien aus freigegebenen Speichern zugreifen.
Berechtigung zur Standortermittlung für Medien
Wenn Ihre App auf Android 10 (API-Level 29) oder höher ausgerichtet ist und
Um nicht entfernte EXIF-Metadaten aus Fotos abzurufen, müssen Sie die
ACCESS_MEDIA_LOCATION
im Manifest Ihrer App ein und fordern Sie diese Berechtigung dann zur Laufzeit an.
Nach Updates für den Medienspeicher suchen
Damit Sie zuverlässiger auf Mediendateien zugreifen können, insbesondere wenn Ihre App URIs oder Daten aus dem Medienspeicher im Cache speichert, prüfen Sie, ob sich die Version des Medienspeichers seit der letzten Synchronisierung Ihrer Mediendaten geändert hat. Um diese Prüfung für
Updates, Anruf
getVersion()
Die zurückgegebene Version ist ein eindeutiger String, der sich jedes Mal ändert, wenn der Medienspeicher
die sich wesentlich ändern. Wenn sich die zurückgegebene Version von der letzten Synchronisierung unterscheidet
suchen Sie den Medien-Cache Ihrer App noch einmal und synchronisieren Sie ihn anschließend neu.
Führen Sie diese Prüfung beim Start des App-Prozesses durch. Es ist nicht nötig, die -Version bei jeder Abfrage des Medienspeichers.
Gehen Sie nicht von Implementierungsdetails in Bezug auf die Versionsnummer aus.
Mediensammlung abfragen
Um Medien zu finden, die eine bestimmte Reihe von Bedingungen erfüllen, z. B. eine Dauer mindestens 5 Minuten lang ist, verwenden Sie eine SQL-ähnliche Auswahlanweisung, wie im folgenden Code-Snippet dargestellt:
Kotlin
// Need the READ_EXTERNAL_STORAGE permission if accessing video files that your // app didn't create. // Container for information about each video. data class Video(val uri: Uri, val name: String, val duration: Int, val size: Int ) val videoList = mutableListOf<Video>() val collection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { MediaStore.Video.Media.getContentUri( MediaStore.VOLUME_EXTERNAL ) } else { MediaStore.Video.Media.EXTERNAL_CONTENT_URI } val projection = arrayOf( MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME, MediaStore.Video.Media.DURATION, MediaStore.Video.Media.SIZE ) // Show only videos that are at least 5 minutes in duration. val selection = "${MediaStore.Video.Media.DURATION} >= ?" val selectionArgs = arrayOf( TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES).toString() ) // Display videos in alphabetical order based on their display name. val sortOrder = "${MediaStore.Video.Media.DISPLAY_NAME} ASC" val query = ContentResolver.query( collection, projection, selection, selectionArgs, sortOrder ) query?.use { cursor -> // Cache column indices. val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID) val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME) val durationColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION) val sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE) while (cursor.moveToNext()) { // Get values of columns for a given video. val id = cursor.getLong(idColumn) val name = cursor.getString(nameColumn) val duration = cursor.getInt(durationColumn) val size = cursor.getInt(sizeColumn) val contentUri: Uri = ContentUris.withAppendedId( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id ) // Stores column values and the contentUri in a local object // that represents the media file. videoList += Video(contentUri, name, duration, size) } }
Java
// Need the READ_EXTERNAL_STORAGE permission if accessing video files that your // app didn't create. // Container for information about each video. class Video { private final Uri uri; private final String name; private final int duration; private final int size; public Video(Uri uri, String name, int duration, int size) { this.uri = uri; this.name = name; this.duration = duration; this.size = size; } } List<Video> videoList = new ArrayList<Video>(); Uri collection; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL); } else { collection = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } String[] projection = new String[] { MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME, MediaStore.Video.Media.DURATION, MediaStore.Video.Media.SIZE }; String selection = MediaStore.Video.Media.DURATION + " >= ?"; String[] selectionArgs = new String[] { String.valueOf(TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES)); }; String sortOrder = MediaStore.Video.Media.DISPLAY_NAME + " ASC"; try (Cursor cursor = getApplicationContext().getContentResolver().query( collection, projection, selection, selectionArgs, sortOrder )) { // Cache column indices. int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID); int nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME); int durationColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION); int sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE); while (cursor.moveToNext()) { // Get values of columns for a given video. long id = cursor.getLong(idColumn); String name = cursor.getString(nameColumn); int duration = cursor.getInt(durationColumn); int size = cursor.getInt(sizeColumn); Uri contentUri = ContentUris.withAppendedId( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id); // Stores column values and the contentUri in a local object // that represents the media file. videoList.add(new Video(contentUri, name, duration, size)); } }
Beachten Sie bei der Durchführung einer solchen Abfrage in Ihrer App Folgendes:
- Rufen Sie die Methode
query()
in einem Worker-Thread auf. - Speichern Sie die Spaltenindexe im Cache, sodass Sie nicht
getColumnIndexOrThrow()
jedes Mal, wenn Sie eine Zeile aus dem Abfrageergebnisse verarbeiten. - Hängen Sie die ID wie in diesem Beispiel an den Inhalts-URI an.
- Für Geräte mit Android 10 und höher ist eine Spalte
Namen, die in
die
MediaStore
API. Wenn eine abhängige Bibliothek in Ihrer App eine Spalte erwartet der in der API nicht definiert ist (z. B."MimeType"
), verwenden SieCursorWrapper
in „dynamisch“ und übersetzen Sie den Spaltennamen im App-Prozess.
Miniaturansichten von Dateien laden
Zeigt Ihre App mehrere Mediendateien an und bittet der Nutzer, eine der ist es effizienter, die Vorschau zu laden, Versionen oder Miniaturansichten des anstelle der Dateien selbst.
Um die Miniaturansicht für eine bestimmte Mediendatei zu laden, verwenden Sie
loadThumbnail()
und übergeben Sie die Größe des zu ladenden Thumbnails, wie in den
folgenden Code-Snippet:
Kotlin
// Load thumbnail of a specific media item. val thumbnail: Bitmap = applicationContext.contentResolver.loadThumbnail( content-uri, Size(640, 480), null)
Java
// Load thumbnail of a specific media item. Bitmap thumbnail = getApplicationContext().getContentResolver().loadThumbnail( content-uri, new Size(640, 480), null);
Mediendatei öffnen
Die spezifische Logik zum Öffnen einer Mediendatei hängt davon ab, Medieninhalte lassen sich am besten als Dateibeschreibung, Dateistream oder direkten Dateipfad.
Dateideskriptor
Wenn du eine Mediendatei mit einem Dateideskriptor öffnen möchtest, verwende eine Logik ähnlich der im folgenden Code-Snippet gezeigten:
Kotlin
// Open a specific media item using ParcelFileDescriptor. val resolver = applicationContext.contentResolver // "rw" for read-and-write. // "rwt" for truncating or overwriting existing file contents. val readOnlyMode = "r" resolver.openFileDescriptor(content-uri, readOnlyMode).use { pfd -> // Perform operations on "pfd". }
Java
// Open a specific media item using ParcelFileDescriptor. ContentResolver resolver = getApplicationContext() .getContentResolver(); // "rw" for read-and-write. // "rwt" for truncating or overwriting existing file contents. String readOnlyMode = "r"; try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(content-uri, readOnlyMode)) { // Perform operations on "pfd". } catch (IOException e) { e.printStackTrace(); }
Dateistream
Um eine Mediendatei mit einem Dateistream zu öffnen, verwenden Sie eine Logik wie die in das folgende Code-Snippet:
Kotlin
// Open a specific media item using InputStream. val resolver = applicationContext.contentResolver resolver.openInputStream(content-uri).use { stream -> // Perform operations on "stream". }
Java
// Open a specific media item using InputStream. ContentResolver resolver = getApplicationContext() .getContentResolver(); try (InputStream stream = resolver.openInputStream(content-uri)) { // Perform operations on "stream". }
Direkte Dateipfade
Damit Ihre App reibungslos mit Mediatheken von Drittanbietern funktioniert,
Mit Android 11 (API-Level 30) und höher können Sie andere APIs als die
MediaStore
API für den Zugriff
Mediendateien aus freigegebenem Speicher. Sie können stattdessen über eine der folgenden APIs direkt auf Mediendateien zugreifen:
- Die
File
API - Native Bibliotheken, z. B.
fopen()
Wenn Sie keine speicherbezogenen Berechtigungen haben, können Sie auf Dateien in Ihrem
app-spezifisches Verzeichnis und Medien
Dateien, die Ihrer App mithilfe der File
API zugeordnet werden.
Wenn deine App versucht, mit der File
API auf eine Datei zuzugreifen, und sie nicht die
Berechtigungen, ein
FileNotFoundException
ist aufgetreten.
Wenn Sie auf andere Dateien im freigegebenen Speicher auf einem Gerät mit Android 10 (API-Level 29) zugreifen möchten, empfehlen wir Ihnen, den speicherortbezogenen Speicher vorübergehend zu deaktivieren. Legen Sie dazu in der Manifestdatei Ihrer App requestLegacyExternalStorage
auf true
fest. Um auf Mediendateien zuzugreifen, verwenden Sie
native Dateimethoden unter Android 10 verwenden, müssen Sie auch
READ_EXTERNAL_STORAGE
Berechtigung.
Überlegungen beim Zugriff auf Medieninhalte
Beachten Sie beim Zugriff auf Medieninhalte die in den folgenden Abschnitten.
Daten im Cache
Wenn Ihre App URIs oder Daten aus dem Medienspeicher im Cache speichert, sollten Sie regelmäßig nach Updates für den Medienspeicher suchen. Durch diese Prüfung bleiben Ihre app-seitigen, im Cache gespeicherten Daten mit den systemseitigen Anbieterdaten synchron.
Leistung
Wenn Sie sequenzielle Lesevorgänge über direkte Dateipfade durchführen,
der Leistung der Kampagne
MediaStore
API
Wenn Sie Mediendateien zufällig über direkte Dateipfade lesen und schreiben,
Dieser Vorgang kann jedoch bis zu doppelt so langsam sein. In diesen Fällen empfehlen wir stattdessen die Verwendung der MediaStore
API.
Spalte DATA
Wenn Sie auf eine vorhandene Mediendatei zugreifen, können Sie den Wert des Parameters
Spalte DATA
in
Ihre Logik. Das liegt daran, dass dieser Wert einen gültigen Dateipfad hat. Achten Sie jedoch darauf,
dass die Datei immer verfügbar ist. Sie müssen darauf vorbereitet sein,
Mögliche E/A-Fehler.
Um eine Mediendatei zu erstellen oder zu aktualisieren, sollten Sie jedoch nicht den Wert des Felds
DATA
. Verwenden Sie stattdessen die Werte der
DISPLAY_NAME
und
RELATIVE_PATH
Spalten.
Speicher-Volumes
Apps, die auf Android 10 oder höher ausgerichtet sind, können auf den eindeutigen Namen zugreifen. die das System jedem externen Speicher-Volume zuweist. Dieses Benennungssystem können Sie Inhalte effizient organisieren und indexieren und haben die Kontrolle wo neue Mediendateien gespeichert werden.
Beachten Sie dabei insbesondere die folgenden Bände:
- Die
VOLUME_EXTERNAL
Volume bietet eine Ansicht aller freigegebenen Speicher-Volumes auf dem Gerät. Sie können Folgendes lesen: des synthetischen Volumes löschen, aber Sie können den Inhalt nicht ändern. - Die
VOLUME_EXTERNAL_PRIMARY
Volume steht für das primäre freigegebene Speicher-Volume auf dem Gerät. Sie können den Inhalt dieses Bandes lesen und ändern.
Sie können weitere Volumes ermitteln, indem Sie
MediaStore.getExternalVolumeNames()
:
Kotlin
val volumeNames: Set<String> = MediaStore.getExternalVolumeNames(context) val firstVolumeName = volumeNames.iterator().next()
Java
Set<String> volumeNames = MediaStore.getExternalVolumeNames(context); String firstVolumeName = volumeNames.iterator().next();
Aufnahmeort der Medien
Einige Fotos und Videos enthalten in ihren Metadaten Informationen zum Aufnahmeort, an dem ein Foto aufgenommen oder ein Video aufgenommen wurde.
Wie Sie in Ihrer App auf diese Standortinformationen zugreifen, hängt davon ab, für ein Foto oder Video benötigen.
Fotos
Wenn Ihre Anwendung eingeschränkten Speicher verwendet, Standortinformationen standardmäßig ausgeblendet. So greifen Sie auf diese Informationen zu:
- Fordern Sie die
ACCESS_MEDIA_LOCATION
im Manifest deiner App. Rufe die genauen Bytes des Fotos aus deinem
MediaStore
-Objekt ab, indem dusetRequireOriginal()
aufrufst und den URI des Fotos übergibst, wie im folgenden Code-Snippet gezeigt:Kotlin
val photoUri: Uri = Uri.withAppendedPath( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getString(idColumnIndex) ) // Get location data using the Exifinterface library. // Exception occurs if ACCESS_MEDIA_LOCATION permission isn't granted. photoUri = MediaStore.setRequireOriginal(photoUri) contentResolver.openInputStream(photoUri)?.use { stream -> ExifInterface(stream).run { // If lat/long is null, fall back to the coordinates (0, 0). val latLong = latLong ?: doubleArrayOf(0.0, 0.0) } }
Java
Uri photoUri = Uri.withAppendedPath( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getString(idColumnIndex)); final double[] latLong; // Get location data using the Exifinterface library. // Exception occurs if ACCESS_MEDIA_LOCATION permission isn't granted. photoUri = MediaStore.setRequireOriginal(photoUri); InputStream stream = getContentResolver().openInputStream(photoUri); if (stream != null) { ExifInterface exifInterface = new ExifInterface(stream); double[] returnedLatLong = exifInterface.getLatLong(); // If lat/long is null, fall back to the coordinates (0, 0). latLong = returnedLatLong != null ? returnedLatLong : new double[2]; // Don't reuse the stream associated with // the instance of "ExifInterface". stream.close(); } else { // Failed to load the stream, so return the coordinates (0, 0). latLong = new double[2]; }
Videos
Um auf Standortinformationen in den Metadaten eines Videos zuzugreifen, verwende die
MediaMetadataRetriever
enthalten, wie im folgenden Code-Snippet gezeigt. Ihre App muss keine
zusätzliche Berechtigungen zur Verwendung dieses Kurses.
Kotlin
val retriever = MediaMetadataRetriever() val context = applicationContext // Find the videos that are stored on a device by querying the video collection. val query = ContentResolver.query( collection, projection, selection, selectionArgs, sortOrder ) query?.use { cursor -> val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID) while (cursor.moveToNext()) { val id = cursor.getLong(idColumn) val videoUri: Uri = ContentUris.withAppendedId( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id ) extractVideoLocationInfo(videoUri) } } private fun extractVideoLocationInfo(videoUri: Uri) { try { retriever.setDataSource(context, videoUri) } catch (e: RuntimeException) { Log.e(APP_TAG, "Cannot retrieve video file", e) } // Metadata uses a standardized format. val locationMetadata: String? = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION) }
Java
MediaMetadataRetriever retriever = new MediaMetadataRetriever(); Context context = getApplicationContext(); // Find the videos that are stored on a device by querying the video collection. try (Cursor cursor = context.getContentResolver().query( collection, projection, selection, selectionArgs, sortOrder )) { int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID); while (cursor.moveToNext()) { long id = cursor.getLong(idColumn); Uri videoUri = ContentUris.withAppendedId( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id); extractVideoLocationInfo(videoUri); } } private void extractVideoLocationInfo(Uri videoUri) { try { retriever.setDataSource(context, videoUri); } catch (RuntimeException e) { Log.e(APP_TAG, "Cannot retrieve video file", e); } // Metadata uses a standardized format. String locationMetadata = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_LOCATION); }
Inhalte teilen
In einigen Apps können Nutzer Mediendateien untereinander freigeben. Zum Beispiel soziale Netzwerke Mit Medien-Apps können Nutzer Fotos und Videos mit Freunden teilen.
Verwende zum Freigeben von Mediendateien einen content://
-URI, wie im Leitfaden zur
Contentanbieter erstellen.
App-Attribution von Mediendateien
Wenn der begrenzte Speicher für eine für Android 10 oder höher verwenden, werden vom System Attribute mit jeder Mediendatei verknüpft. Dadurch wird festgelegt, auf welche Dateien Ihre App zugreifen kann, wenn Es wurden keine Speicherberechtigungen angefordert. Jede Datei kann nur eine App. Wenn Ihre App also eine Mediendatei erstellt, die im Fotos, Videos oder Audiodateien erfassen möchten, hat deine App Zugriff auf die -Datei.
Deinstalliert der Nutzer die App und installiert sie anschließend neu, musst du eine entsprechende Anfrage senden.
READ_EXTERNAL_STORAGE
um auf die Dateien zuzugreifen, die deine App ursprünglich erstellt hat. Diese Berechtigungsanfrage ist
erforderlich, da das System die Datei
eine bereits installierte Version der App.
Artikel hinzufügen
Um ein Medienelement zu einer vorhandenen Sammlung hinzuzufügen, verwenden Sie Code ähnlich dem
folgen. Dieses Code-Snippet greift auf das Volume VOLUME_EXTERNAL_PRIMARY
zu
auf Geräten mit Android 10 oder höher. Das liegt daran, dass Sie auf diesen Geräten
kann den Inhalt eines Volumes nur ändern, wenn es das primäre Volume ist,
wie im Abschnitt Speichervolumen beschrieben.
Kotlin
// Add a specific media item. val resolver = applicationContext.contentResolver // Find all audio files on the primary external storage device. val audioCollection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { MediaStore.Audio.Media.getContentUri( MediaStore.VOLUME_EXTERNAL_PRIMARY ) } else { MediaStore.Audio.Media.EXTERNAL_CONTENT_URI } // Publish a new song. val newSongDetails = ContentValues().apply { put(MediaStore.Audio.Media.DISPLAY_NAME, "My Song.mp3") } // Keep a handle to the new song's URI in case you need to modify it // later. val myFavoriteSongUri = resolver .insert(audioCollection, newSongDetails)
Java
// Add a specific media item. ContentResolver resolver = getApplicationContext() .getContentResolver(); // Find all audio files on the primary external storage device. Uri audioCollection; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { audioCollection = MediaStore.Audio.Media .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY); } else { audioCollection = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } // Publish a new song. ContentValues newSongDetails = new ContentValues(); newSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME, "My Song.mp3"); // Keep a handle to the new song's URI in case you need to modify it // later. Uri myFavoriteSongUri = resolver .insert(audioCollection, newSongDetails);
Status „Ausstehend“ für Mediendateien aktivieren/deaktivieren
Wenn Ihre Anwendung potenziell zeitaufwendige Vorgänge ausführt,
Mediendateien sind, ist es nützlich, exklusiven Zugriff auf die Datei zu haben,
verarbeitet werden. Auf Geräten mit Android 10 oder höher kann Ihre App
erhalten Sie diesen exklusiven Zugriff, indem Sie den Wert
IS_PENDING
Flag auf 1 setzen. Nur Ihre App kann die Datei ansehen, bis Ihre App den Wert von
IS_PENDING
auf 0 zurücksetzen.
Das folgende Code-Snippet baut auf dem vorherigen Code-Snippet auf. In diesem Snippet wird gezeigt, wie du das Flag IS_PENDING
verwendest, wenn du einen langen Titel im Verzeichnis speicherst, das der Sammlung MediaStore.Audio
entspricht:
Kotlin
// Add a media item that other apps don't see until the item is // fully written to the media store. val resolver = applicationContext.contentResolver // Find all audio files on the primary external storage device. val audioCollection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { MediaStore.Audio.Media.getContentUri( MediaStore.VOLUME_EXTERNAL_PRIMARY ) } else { MediaStore.Audio.Media.EXTERNAL_CONTENT_URI } val songDetails = ContentValues().apply { put(MediaStore.Audio.Media.DISPLAY_NAME, "My Workout Playlist.mp3") put(MediaStore.Audio.Media.IS_PENDING, 1) } val songContentUri = resolver.insert(audioCollection, songDetails) // "w" for write. resolver.openFileDescriptor(songContentUri, "w", null).use { pfd -> // Write data into the pending audio file. } // Now that you're finished, release the "pending" status and let other apps // play the audio track. songDetails.clear() songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0) resolver.update(songContentUri, songDetails, null, null)
Java
// Add a media item that other apps don't see until the item is // fully written to the media store. ContentResolver resolver = getApplicationContext() .getContentResolver(); // Find all audio files on the primary external storage device. Uri audioCollection; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { audioCollection = MediaStore.Audio.Media .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY); } else { audioCollection = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } ContentValues songDetails = new ContentValues(); songDetails.put(MediaStore.Audio.Media.DISPLAY_NAME, "My Workout Playlist.mp3"); songDetails.put(MediaStore.Audio.Media.IS_PENDING, 1); Uri songContentUri = resolver .insert(audioCollection, songDetails); // "w" for write. try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(songContentUri, "w", null)) { // Write data into the pending audio file. } // Now that you're finished, release the "pending" status and let other apps // play the audio track. songDetails.clear(); songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0); resolver.update(songContentUri, songDetails, null, null);
Geben Sie einen Hinweis auf den Speicherort einer Datei
Wenn deine App Medien auf einem Gerät mit Android 10 speichert, gilt Folgendes:
sind die Medien standardmäßig nach ihrem Typ organisiert. Zum Beispiel ist standardmäßig
werden Bilddateien im
Environment.DIRECTORY_PICTURES
Verzeichnis, das dem
Sammlung MediaStore.Images
.
Wenn Ihre App einen bestimmten Speicherort kennt, an dem Dateien gespeichert werden können, z. B. ein Fotoalbum namens Pictures/MyVacationPictures
, können Sie MediaColumns.RELATIVE_PATH
festlegen, um dem System einen Hinweis darauf zu geben, wo die neu geschriebenen Dateien gespeichert werden sollen.
Artikel aktualisieren
Um eine Mediendatei zu aktualisieren, die Ihrer App gehört, verwenden Sie Code ähnlich dem folgenden:
Kotlin
// Updates an existing media item. val mediaId = // MediaStore.Audio.Media._ID of item to update. val resolver = applicationContext.contentResolver // When performing a single item update, prefer using the ID. val selection = "${MediaStore.Audio.Media._ID} = ?" // By using selection + args you protect against improper escaping of // values. val selectionArgs = arrayOf(mediaId.toString()) // Update an existing song. val updatedSongDetails = ContentValues().apply { put(MediaStore.Audio.Media.DISPLAY_NAME, "My Favorite Song.mp3") } // Use the individual song's URI to represent the collection that's // updated. val numSongsUpdated = resolver.update( myFavoriteSongUri, updatedSongDetails, selection, selectionArgs)
Java
// Updates an existing media item. long mediaId = // MediaStore.Audio.Media._ID of item to update. ContentResolver resolver = getApplicationContext() .getContentResolver(); // When performing a single item update, prefer using the ID. String selection = MediaStore.Audio.Media._ID + " = ?"; // By using selection + args you protect against improper escaping of // values. Here, "song" is an in-memory object that caches the song's // information. String[] selectionArgs = new String[] { getId().toString() }; // Update an existing song. ContentValues updatedSongDetails = new ContentValues(); updatedSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME, "My Favorite Song.mp3"); // Use the individual song's URI to represent the collection that's // updated. int numSongsUpdated = resolver.update( myFavoriteSongUri, updatedSongDetails, selection, selectionArgs);
Wenn begrenzter Speicher nicht verfügbar oder nicht aktiviert ist, wird der im das Sie auch für Dateien verwenden können, die nicht Ihrer App gehören.
Im nativen Code aktualisieren
Wenn Sie Mediendateien mit nativen Bibliotheken schreiben müssen, übergeben Sie zugehörigen Dateideskriptor aus Ihrem Java- oder Kotlin-basierten Code in Ihren nativen Code.
Das folgende Code-Snippet zeigt, wie der Dateideskriptor eines Medienobjekts übergeben wird. in den nativen Code Ihrer App einfügen:
Kotlin
val contentUri: Uri = ContentUris.withAppendedId( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, cursor.getLong(BaseColumns._ID)) val fileOpenMode = "r" val parcelFd = resolver.openFileDescriptor(contentUri, fileOpenMode) val fd = parcelFd?.detachFd() // Pass the integer value "fd" into your native code. Remember to call // close(2) on the file descriptor when you're done using it.
Java
Uri contentUri = ContentUris.withAppendedId( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, cursor.getLong(Integer.parseInt(BaseColumns._ID))); String fileOpenMode = "r"; ParcelFileDescriptor parcelFd = resolver.openFileDescriptor(contentUri, fileOpenMode); if (parcelFd != null) { int fd = parcelFd.detachFd(); // Pass the integer value "fd" into your native code. Remember to call // close(2) on the file descriptor when you're done using it. }
Andere Apps aktualisieren“ Mediendateien
Wenn Ihre Anwendung eingeschränkten Speicher verwendet, Mediendateien, die von einer anderen App zur Datei beigetragen haben, können normalerweise nicht aktualisiert werden. Medienspeicher.
Sie können jedoch die Zustimmung des Nutzers zum Ändern der Datei einholen, indem Sie den
RecoverableSecurityException
die die Plattform wirft. Sie können dann den Nutzer bitten, Ihrer App Schreibzugriff auf das betreffende Element zu gewähren, wie im folgenden Code-Snippet gezeigt:
Kotlin
// Apply a grayscale filter to the image at the given content URI. try { // "w" for write. contentResolver.openFileDescriptor(image-content-uri, "w")?.use { setGrayscaleFilter(it) } } catch (securityException: SecurityException) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val recoverableSecurityException = securityException as? RecoverableSecurityException ?: throw RuntimeException(securityException.message, securityException) val intentSender = recoverableSecurityException.userAction.actionIntent.intentSender intentSender?.let { startIntentSenderForResult(intentSender, image-request-code, null, 0, 0, 0, null) } } else { throw RuntimeException(securityException.message, securityException) } }
Java
try { // "w" for write. ParcelFileDescriptor imageFd = getContentResolver() .openFileDescriptor(image-content-uri, "w"); setGrayscaleFilter(imageFd); } catch (SecurityException securityException) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { RecoverableSecurityException recoverableSecurityException; if (securityException instanceof RecoverableSecurityException) { recoverableSecurityException = (RecoverableSecurityException)securityException; } else { throw new RuntimeException( securityException.getMessage(), securityException); } IntentSender intentSender =recoverableSecurityException.getUserAction() .getActionIntent().getIntentSender(); startIntentSenderForResult(intentSender, image-request-code, null, 0, 0, 0, null); } else { throw new RuntimeException( securityException.getMessage(), securityException); } }
Führen Sie diesen Vorgang jedes Mal aus, wenn Ihre App eine Mediendatei ändern muss, die nicht von ihr erstellt wurde.
Wenn Ihre App unter Android 11 oder höher läuft, können Sie
Nutzer können deiner App Schreibzugriff auf eine Gruppe von Mediendateien gewähren. Verwenden Sie die Methode
createWriteRequest()
wie im Abschnitt zur Verwaltung von Mediengruppen beschrieben.
Dateien.
Wenn Ihre App einen anderen Anwendungsfall hat, der nicht vom Speicher mit begrenztem Zugriff abgedeckt ist, reichen Sie einen Funktionsantrag ein und deaktivieren Sie den Speicher mit begrenztem Zugriff vorübergehend.
Elemente entfernen
Wenn Sie einen Artikel aus dem Medienspeicher entfernen möchten, der für Ihre App nicht mehr benötigt wird, verwenden Sie eine Logik, die der im folgenden Code-Snippet gezeigten ähnelt:
Kotlin
// Remove a specific media item. val resolver = applicationContext.contentResolver // URI of the image to remove. val imageUri = "..." // WHERE clause. val selection = "..." val selectionArgs = "..." // Perform the actual removal. val numImagesRemoved = resolver.delete( imageUri, selection, selectionArgs)
Java
// Remove a specific media item. ContentResolver resolver = getApplicationContext() getContentResolver(); // URI of the image to remove. Uri imageUri = "..."; // WHERE clause. String selection = "..."; String[] selectionArgs = "..."; // Perform the actual removal. int numImagesRemoved = resolver.delete( imageUri, selection, selectionArgs);
Wenn begrenzter Speicher nicht verfügbar oder nicht aktiviert ist, können Sie die oben
Code-Snippet verwenden, um Dateien zu entfernen, die anderen Apps gehören. Wenn begrenzter Speicher aktiviert ist,
Sie müssen jedoch für jede Datei, dieRecoverableSecurityException
die Ihre App entfernen möchte, wie im Abschnitt zum Aktualisieren von Medien beschrieben.
Elemente.
Wenn Ihre App unter Android 11 oder höher läuft, können Sie Nutzern erlauben,
und wählen Sie die zu entfernenden Mediendateien aus. Verwenden Sie den createTrashRequest()
.
oder die Methode
createDeleteRequest()
wie im Abschnitt zur Verwaltung von Mediengruppen beschrieben.
Dateien.
Wenn es für Ihre Anwendung einen anderen Anwendungsfall gibt, der nicht durch den begrenzten Speicher abgedeckt ist, reichen Sie einen Funktionsanfrage und begrenzten Zugriff vorübergehend deaktivieren Speicherplatz.
Updates für Mediendateien erkennen
Ihre App muss möglicherweise Speicher-Volumes mit Mediendateien identifizieren, die von Apps installiert werden
im Vergleich zu einem
vorherigen Zeitpunkt hinzugefügt oder geändert haben. Um diese Änderungen zu erkennen
die zuverlässigsten sind, können Sie das gewünschte Speichervolumen
getGeneration()
Solange sich die Version des Medienspeichers nicht ändert, steigt der Rückgabewert dieser Methode mit der Zeit monoton an.
Insbesondere ist getGeneration()
zuverlässiger als die Datumsangaben in Medienspalten.
zum Beispiel
DATE_ADDED
und DATE_MODIFIED
.
Das liegt daran, dass sich diese Medienspaltenwerte ändern können, wenn eine App
setLastModified()
oder wenn
Der Nutzer ändert die Systemuhr.
Gruppen von Mediendateien verwalten
Unter Android 11 und höher können Sie den Nutzer bitten, eine Gruppe von Mediendateien auszuwählen, und diese dann in einem einzigen Vorgang aktualisieren. Diese -Methoden bieten eine bessere Einheitlichkeit auf allen Geräten und die Methoden vereinfachen damit Nutzer ihre Mediensammlungen verwalten können.
Die Methoden, die dieses "Batch-Update" bereitstellen Funktionen umfassen die Folgendes:
createWriteRequest()
- Bitten Sie den Nutzer, Ihrer App Schreibzugriff auf die angegebene Gruppe von Mediendateien zu gewähren.
createFavoriteRequest()
- Der Nutzer soll die angegebenen Mediendateien als einige seiner eigenen "Favorit" Medien auf dem Gerät. Jede App mit Lesezugriff auf diese Datei kann Sie sehen, dass der Nutzer die Datei als „Favorit“ markiert hat.
createTrashRequest()
Bitten Sie den Nutzer, die angegebenen Mediendateien in den Papierkorb des Geräts zu verschieben. Elemente im Papierkorb werden nach einem systemdefinierten Zeitraum endgültig gelöscht.
createDeleteRequest()
Bitten Sie den Nutzer, die angegebenen Mediendateien sofort endgültig zu löschen, ohne sie vorher in den Papierkorb zu verschieben.
Nach dem Aufrufen einer dieser Methoden erstellt das System eine
PendingIntent
-Objekt. Nach der Anwendung
ruft diesen Intent auf und Nutzer sehen ein Dialogfeld, in dem sie um ihre Einwilligung für Ihre App gebeten werden.
, um die angegebenen Mediendateien zu aktualisieren oder zu löschen.
So strukturieren Sie beispielsweise einen Aufruf von createWriteRequest()
:
Kotlin
val urisToModify = /* A collection of content URIs to modify. */ val editPendingIntent = MediaStore.createWriteRequest(contentResolver, urisToModify) // Launch a system prompt requesting user permission for the operation. startIntentSenderForResult(editPendingIntent.intentSender, EDIT_REQUEST_CODE, null, 0, 0, 0)
Java
List<Uri> urisToModify = /* A collection of content URIs to modify. */ PendingIntent editPendingIntent = MediaStore.createWriteRequest(contentResolver, urisToModify); // Launch a system prompt requesting user permission for the operation. startIntentSenderForResult(editPendingIntent.getIntentSender(), EDIT_REQUEST_CODE, null, 0, 0, 0);
Bewerten Sie die Antwort des Nutzers. Wenn der Nutzer eingewilligt hat, fahren Sie mit der Medienbetrieb. Erklären Sie andernfalls dem Nutzer, warum Ihre App die Berechtigung:
Kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { ... when (requestCode) { EDIT_REQUEST_CODE -> if (resultCode == Activity.RESULT_OK) { /* Edit request granted; proceed. */ } else { /* Edit request not granted; explain to the user. */ } } }
Java
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { ... if (requestCode == EDIT_REQUEST_CODE) { if (resultCode == Activity.RESULT_OK) { /* Edit request granted; proceed. */ } else { /* Edit request not granted; explain to the user. */ } } }
Sie können dasselbe allgemeine Muster
createFavoriteRequest()
,
createTrashRequest()
,
und
createDeleteRequest()
Berechtigung für die Medienverwaltung
Nutzer vertrauen möglicherweise einer bestimmten App für die Medienverwaltung, z. B. häufig bearbeitete Mediendateien. Wenn Ihre App auf Android 11 oder höher und nicht die Standardgalerie-App des Geräts. Sie müssen dem Nutzer jedes Mal ein Bestätigungsdialogfeld anzeigen, wenn Ihre App versucht, zu ändern oder zu löschen.
Wenn Ihre App auf Android 12 (API-Level 31) oder höher ausgerichtet ist, können Sie -Nutzer Ihrer App Zugriff auf die spezielle Berechtigung Medienverwaltung gewähren. Dieses Mit dieser Berechtigung kann deine App Folgendes ohne Eingabeaufforderung ausführen für jeden Dateivorgang:
- Ändern von Dateien mithilfe von
createWriteRequest()
- Verschieben Sie Dateien mit
createTrashRequest()
in den Papierkorb und wieder daraus. - Dateien löschen mit
createDeleteRequest()
Führen Sie dazu die folgenden Schritte aus:
Deklarieren Sie die Berechtigung
MANAGE_MEDIA
und die BerechtigungREAD_EXTERNAL_STORAGE
in der Manifestdatei Ihrer App.createWriteRequest()
ohne Bestätigung anrufen deklarieren Sie dieACCESS_MEDIA_LOCATION
um eine Genehmigung zu erhalten.Zeige dem Nutzer in deiner App eine Benutzeroberfläche, in der er erklärt, warum er ihm Zugriff auf die Medienverwaltung deiner App.
Rufen Sie die Methode
ACTION_REQUEST_MANAGE_MEDIA
Intent-Aktion. Dadurch gelangen Nutzer zum Bildschirm Apps für die Medienverwaltung in Systemeinstellungen. Von hier aus können Nutzer der speziellen App Zugriff gewähren.
Anwendungsfälle, für die eine Alternative zum Medienspeicher erforderlich ist
Wenn Ihre App hauptsächlich eine der folgenden Rollen erfüllt, sollten Sie einen
zu den MediaStore
-APIs.
Mit anderen Dateitypen arbeiten
Wenn Ihre App mit Dokumenten und Dateien funktioniert, die nicht ausschließlich Medieninhalte enthalten, z. B. Dateien mit der Dateiendung EPUB oder PDF, verwenden Sie die Intent-Aktion ACTION_OPEN_DOCUMENT
, wie im Leitfaden zum Speichern und Zugriff auf Dokumente und andere Dateien beschrieben.
Dateifreigabe in Companion-Apps
Wenn Sie eine Suite von Companion-Apps bereitstellen, z. B. eine Messaging-App und
eine Profil-App, richten Sie die Dateifreigabe ein.
mit content://
-URIs. Wir empfehlen diesen Workflow auch als Best Practice für die Sicherheit.
Übung.
Weitere Informationen
Weitere Informationen zum Speichern und Zugreifen auf Medien finden Sie hier: Ressourcen.
Produktproben
- MediaStore verfügbar auf GitHub