Per offrire un'esperienza utente più completa, molte app consentono agli utenti di contribuire e accedere ai contenuti multimediali disponibili su un volume di archiviazione esterno. Il framework fornisce un indice ottimizzato nelle raccolte di contenuti multimediali, chiamate media store, che consente agli utenti di recuperare e aggiornare più facilmente questi file multimediali. Uniforme Dopo la disinstallazione dell'app, questi file rimangono sul dispositivo dell'utente.
Selettore di foto
In alternativa al media store, lo strumento di selezione delle foto Android offre agli utenti un modo sicuro e integrato per selezionare i file multimediali senza dover per concedere alla tua app l'accesso all'intera raccolta multimediale. Questa opzione è disponibile solo sui dispositivi supportati. Per ulteriori informazioni, consulta selettore di foto.
Negozio multimediale
Per interagire con l'astrazione del negozio multimediale, utilizza un
ContentResolver
oggetto di cui
recupera dal contesto della tua app:
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. }
Il sistema analizza automaticamente il volume di una memoria esterna e aggiunge i file multimediali alle seguenti raccolte ben definite:
- Immagini,comprese fotografie e screenshot, che vengono archiviate nell'
Directory
DCIM/
ePictures/
. Il sistema aggiunge questi file alla tabellaMediaStore.Images
. - Video memorizzati in
DCIM/
,Movies/
ePictures/
. Il sistema aggiunge questi file alla tabellaMediaStore.Video
. - File audio, archiviati nelle directory
Alarms/
,Audiobooks/
,Music/
,Notifications/
,Podcasts/
eRingtones/
. Inoltre, sistema riconosce le playlist audio presenti nell'elencoMusic/
oMovies/
e le registrazioni vocali che si trovano nell'elencoRecordings/
. Il sistema aggiunge questi file TabellaMediaStore.Audio
. La directoryRecordings/
non è disponibile su Android 11 (livello API 30) e versioni precedenti. - File scaricati, archiviati nella directory
Download/
. Attivato sui dispositivi con Android 10 (livello API 29) e versioni successive, questi file vengono archiviati nell'MediaStore.Downloads
tabella. Questa tabella non è disponibile su Android 9 (livello API 28) e versioni precedenti.
Il media store include anche una raccolta chiamata
MediaStore.Files
I suoi contenuti
a seconda che l'app utilizzi o meno l'ambito
spazio di archiviazione, disponibile sulle app che hanno come target
Android 10 o versioni successive.
- Se lo spazio di archiviazione limitato è attivo, la raccolta mostra solo le foto, i video e i file audio creati dalla tua app. La maggior parte degli sviluppatori non ha bisogno di
MediaStore.Files
per visualizzare i file multimediali da altre app, ma se disponi di un requisito specifico per farlo, puoi dichiarare l'READ_EXTERNAL_STORAGE
autorizzazione. Tuttavia, ti consigliamo di utilizzare loMediaStore
API per aprire file non creati dalla tua app. - Se l'archiviazione con ambito non è disponibile o non è in uso, la raccolta mostra tutte tipi di file multimediali.
Richiedi le autorizzazioni necessarie
Prima di eseguire operazioni sui file multimediali, assicurati che la tua app abbia dichiarato le autorizzazioni necessarie per accedere a questi file. Fai attenzione, tuttavia, a dichiarare le autorizzazioni necessarie o non necessarie dall'app.
Autorizzazioni di archiviazione
La necessità di autorizzazioni per accedere allo spazio di archiviazione da parte di un'app dipende dal fatto che possa eseguire l'accesso solo i propri file multimediali o quelli creati da altre app.
Accedere ai tuoi file multimediali
Sui dispositivi con Android 10 o versioni successive, non è necessario
relative allo spazio di archiviazione per accedere e modificare i file multimediali che
di proprietà della tua app, inclusi i file nell'MediaStore.Downloads
. Ad esempio, se stai sviluppando un'app per la fotocamera, non è necessario richiedere autorizzazioni relative allo spazio di archiviazione per accedere alle foto scattate, perché la tua app è proprietaria delle immagini che stai scrivendo nel media store.
Accedere ai file multimediali di altre app
Per accedere ai file multimediali creati da altre app, devi dichiarare le autorizzazioni appropriate relative allo spazio di archiviazione e i file devono trovarsi in una delle seguenti raccolte multimediali:
Finché un file è visualizzabile da MediaStore.Images
,
MediaStore.Video
, o MediaStore.Audio
, possono essere visualizzate anche utilizzando
MediaStore.Files
.
Il seguente snippet di codice mostra come dichiarare lo spazio di archiviazione appropriato autorizzazioni:
<!-- 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" />
Autorizzazioni aggiuntive necessarie per le app in esecuzione sui dispositivi precedenti
Se la tua app viene utilizzata su un dispositivo con Android 9 o versioni precedenti o se
la tua app ha temporaneamente disattivato l'ambito
di archiviazione, devi
richiedi il
READ_EXTERNAL_STORAGE
:
per accedere ai file multimediali. Se vuoi modificare i file multimediali, devi
richiedi il
WRITE_EXTERNAL_STORAGE
autorizzazione.
Storage Access Framework richiesto per accedere ad altre app download
Se la tua app vuole accedere a un file all'interno della raccolta MediaStore.Downloads
non creato dalla tua app, devi usare Storage Access Framework. Per ulteriori informazioni
di più su come utilizzare questo framework, vedi Accedere a documenti e altri file da
spazio di archiviazione condiviso.
Autorizzazione di accesso alla posizione dei contenuti multimediali
Se la tua app ha come target Android 10 (livello API 29) o versioni successive e deve recuperare i metadati EXIF non oscurati dalle foto, devi dichiarare l'autorizzazione ACCESS_MEDIA_LOCATION
nel file manifest dell'app e poi richiederla in fase di esecuzione.
Verificare la disponibilità di aggiornamenti per il media store
Per accedere ai file multimediali in modo più affidabile, in particolare se la tua app memorizza gli URI nella cache
dal media store, controlla se la versione del media store è cambiata
rispetto all'ultima sincronizzazione dei tuoi dati multimediali. Per eseguire questo controllo per gli aggiornamenti, chiama getVersion()
.
La versione restituita è una stringa univoca che cambia ogni volta che il media store cambia in modo sostanziale. Se la versione restituita è diversa da quella dell'ultima sincronizzazione
scansiona e sincronizza di nuovo la cache multimediale dell'app.
Completa questo controllo quando viene avviato il processo dell'app. Non è necessario controllare ogni volta che esegui una query sul media store.
Non dare per scontato alcun dettaglio di implementazione relativo al numero di versione.
Eseguire query su una raccolta di contenuti multimediali
Per trovare contenuti multimediali che soddisfano un determinato insieme di condizioni, ad esempio una durata minima di 5 minuti, utilizza un'istruzione di selezione simile a SQL, simile a quella riportata nel seguente snippet di codice:
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)); } }
Quando esegui una query di questo tipo nella tua app, tieni presente quanto segue:
- Chiama il metodo
query()
in un thread di lavoro. - Memorizza nella cache gli indici delle colonne in modo da non dover chiamare
getColumnIndexOrThrow()
ogni volta che elabori una riga del risultato della query. - Aggiungi l'ID all'URI dei contenuti come mostrato in questo esempio.
- I dispositivi con Android 10 e versioni successive richiedono colonna
che sono definiti
l'API
MediaStore
. Se una libreria dipendente all'interno dell'app prevede una colonna che non è definito nell'API, come"MimeType"
, usaCursorWrapper
in modo dinamico tradurre il nome della colonna durante la procedura dell'app.
Carica miniature di file
Se la tua app mostra più file multimediali e richiede all'utente di sceglierne uno, è più efficiente caricare le versioni di anteprima, o le miniature, dei file anziché i file stessi.
Per caricare la miniatura di un determinato file multimediale, utilizza
loadThumbnail()
e passa le dimensioni della miniatura che vuoi caricare, come mostrato nel
seguente snippet di codice:
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);
Aprire un file multimediale
La logica specifica utilizzata per aprire un file multimediale dipende dal fatto che la i contenuti multimediali sono rappresentati al meglio come descrittore di file, stream di file o percorso del file diretto.
Descrittore del file
Per aprire un file multimediale utilizzando un descrittore del file, utilizza una logica simile a quella mostrata in il seguente snippet di codice:
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(); }
Stream di file
Per aprire un file multimediale utilizzando uno stream di file, utilizza una logica simile a quella mostrata nel seguente snippet di codice:
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". }
Percorsi file diretti
Per fare in modo che la tua app funzioni più facilmente con le raccolte multimediali di terze parti,
Android 11 (livello API 30) e versioni successive ti consentono di utilizzare API diverse da
API MediaStore
per accedere
i file multimediali dallo spazio di archiviazione condiviso. Puoi invece accedere direttamente ai file multimediali
utilizzando una delle seguenti API:
- L'API
File
- Librerie native, ad esempio
fopen()
Se non disponi di autorizzazioni relative allo spazio di archiviazione, puoi accedere ai file nel tuo
directory specifica dell'app, nonché contenuti multimediali
attribuiti alla tua app tramite l'API File
.
Se la tua app tenta di accedere a un file utilizzando l'API File
e non dispone delle autorizzazioni necessarie, viene visualizzato un messaggio FileNotFoundException
.
Per accedere ad altri file nello spazio di archiviazione condiviso su un dispositivo con Android 10 (API
livello 29), ti consigliamo di disattivare temporaneamente la funzionalità
spazio di archiviazione impostando
requestLegacyExternalStorage
:
a true
nel file manifest dell'app. Per accedere ai file multimediali utilizzando
file nativi su Android 10, devi richiedere anche
READ_EXTERNAL_STORAGE
autorizzazione.
Considerazioni sull'accesso ai contenuti multimediali
Quando accedi ai contenuti multimediali, tieni presenti le considerazioni riportate nelle seguenti sezioni.
Dati memorizzati nella cache
Se la tua app memorizza nella cache URI o dati del media store, verifica periodicamente la presenza di al media store. Questo controllo consente agli utenti I dati memorizzati nella cache lato app rimangono sincronizzati con i dati del fornitore lato sistema.
Prestazioni
Quando esegui letture sequenziali dei file multimediali utilizzando percorsi diretti dei file,
è paragonabile a quello della
API MediaStore
.
Tuttavia, quando esegui letture e scritture casuali di file multimediali utilizzando percorsi di file diretti, il processo può essere fino al doppio più lento. In queste situazioni,
consigliamo di utilizzare l'API MediaStore
.
Colonna DATA
Quando accedi a un file multimediale esistente, puoi utilizzare il valore della colonna DATA
nella tua logica. Questo perché questo valore ha un percorso file valido. Tuttavia,
supporre che il file sia sempre disponibile. Preparati a gestire qualsiasi tipo di
che si verificano.
Per creare o aggiornare un file multimediale, invece, non utilizzare il valore della colonna DATA
. Utilizza invece i valori delle colonne
DISPLAY_NAME
e
RELATIVE_PATH
.
Volumi di archiviazione
Le app destinate ad Android 10 o versioni successive possono accedere al nome univoco che il sistema assegna a ciascun volume di archiviazione esterna. Questo sistema di denominazione ti aiuta a organizzare e indicizzare in modo efficiente i contenuti e ti offre il controllo in cui sono archiviati i nuovi file multimediali.
I seguenti volumi sono particolarmente utili da tenere presente:
- La
VOLUME_EXTERNAL
consente di visualizzare tutti i volumi dello spazio di archiviazione condiviso sul dispositivo. Puoi leggere i contenuti di questo volume sintetico, ma non puoi modificarli. - La
VOLUME_EXTERNAL_PRIMARY
rappresenta il volume principale dello spazio di archiviazione condiviso sul dispositivo. Puoi leggere e modificare i contenuti di questo volume.
Puoi scoprire altri volumi chiamando
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();
Località in cui sono stati acquisiti i contenuti multimediali
Alcuni video e fotografie contengono informazioni sulla posizione nei metadati, che mostra il luogo in cui è stata scattata una foto o dove è stato registrato un video registrato.
La modalità di accesso a queste informazioni sulla posizione nella tua app dipende dal fatto che tu bisogno di accedere ai dati sulla posizione per una foto o per un video.
Fotografie
Se la tua app utilizza l'archiviazione con ambito, di sistema nasconde le informazioni sulla posizione per impostazione predefinita. Per accedere a queste informazioni, completa i seguenti passaggi:
- Richiedi il
ACCESS_MEDIA_LOCATION
nel file manifest dell'app. Dall'oggetto
MediaStore
, ottieni i byte esatti della fotografia chiamatasetRequireOriginal()
e passando l'URI della fotografia, come mostrato nello snippet di codice seguente: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]; }
Video
Per accedere alle informazioni sulla posizione all'interno dei metadati di un video, utilizza l'icona
MediaMetadataRetriever
come mostrato nello snippet di codice riportato di seguito. L'app non deve richiedere
eventuali autorizzazioni aggiuntive
per utilizzare questo corso.
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); }
Condivisione
Alcune app consentono agli utenti di condividere file multimediali tra loro. Ad esempio, social le app multimediali consentono agli utenti di condividere foto e video con gli amici.
Per condividere file multimediali, utilizza un URI content://
, come consigliato nella guida alle
la creazione di un fornitore di contenuti.
Attribuzione di file multimediali da app
Quando l'archiviazione con ambito è abilitata per un che ha come target Android 10 o versioni successive, il sistema attribuisce un app per ogni file multimediale, il che determina i file a cui la tua app può accedere quando non ha richiesto autorizzazioni per lo spazio di archiviazione. Ogni file può essere attribuito solo un'app. Di conseguenza, se l'app crea un file multimediale che viene archiviato raccolta multimediale di foto, video o file audio, la tua app ha accesso .
Tuttavia, se l'utente disinstalla e reinstalla la tua app, devi richiedere
READ_EXTERNAL_STORAGE
per accedere ai file creati inizialmente dalla tua app. Questa richiesta di autorizzazione è
necessaria perché il sistema considera il file come attribuito
dell'app installata in precedenza, anziché quella appena installata.
Aggiungere un articolo
Per aggiungere un elemento multimediale a una raccolta esistente, utilizza un codice simile a
seguire. Questo snippet di codice accede al volume VOLUME_EXTERNAL_PRIMARY
su dispositivi con Android 10 o versioni successive. Questo perché su questi dispositivi puoi modificare i contenuti di un volume solo se si tratta del volume principale, come descritto nella sezione Volumi di archiviazione.
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);
Attiva/disattiva lo stato in attesa per i file multimediali
Se l'app esegue operazioni potenzialmente dispendiose in termini di tempo, come scrivere a
file multimediali, è utile avere accesso esclusivo al file così com'è
elaborati. Sui dispositivi con Android 10 o versioni successive, la tua app può
per ottenere questo accesso esclusivo impostando il valore
IS_PENDING
su 1. Solo la tua app può visualizzare il file finché l'app non cambia il valore di
IS_PENDING
torna a 0.
Il seguente snippet di codice si basa sul precedente snippet di codice. Questo snippet mostra come utilizzare il flag IS_PENDING
per archiviare un brano lungo nella directory corrispondente alla raccolta MediaStore.Audio
:
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);
Fornisci un suggerimento per la posizione del file
Quando l'app archivia i contenuti multimediali su un dispositivo con Android 10,
per impostazione predefinita i contenuti multimediali
sono organizzati in base al tipo. Ad esempio, per impostazione predefinita,
i file immagine vengono posizionati
Environment.DIRECTORY_PICTURES
che corrisponde alla directory
raccolta MediaStore.Images
.
Se la tua app è a conoscenza di una posizione specifica in cui è possibile archiviare i file, ad esempio un album di foto denominato Pictures/MyVacationPictures
, puoi impostare MediaColumns.RELATIVE_PATH
per fornire al sistema un suggerimento su dove archiviare i file appena scritti.
Aggiornare un elemento
Per aggiornare un file multimediale di proprietà della tua app, usa un codice simile al seguente:
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);
Se l'archiviazione con ambito non è disponibile o non è abilitata, il processo mostrato in lo snippet di codice precedente funziona anche per i file non di proprietà della tua app.
Aggiorna nel codice nativo
Se devi scrivere file multimediali utilizzando librerie native, passa il parametro descrittore di file associato dal codice basato su Java o Kotlin nel tuo codice nativo.
Il seguente snippet di codice mostra come passare il descrittore del file di un oggetto multimediale nel codice nativo della tua app:
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. }
Aggiornare i file multimediali di altre app
Se la tua app utilizza l'archiviazione con ambito, di solito non è possibile aggiornare un file multimediale che un'altra app ha contribuito alla un media store.
Puoi però ottenere il consenso dell'utente per modificare il file rilevando
RecoverableSecurityException
offerti dalla piattaforma. Puoi quindi richiedere all'utente di concedere la tua app
l'accesso in scrittura all'elemento specifico, come mostrato nello snippet di codice riportato di seguito:
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); } }
Completa questa procedura ogni volta che la tua app deve modificare un file multimediale che non ha creato.
In alternativa, se la tua app funziona su Android 11 o versioni successive, puoi:
Consenti agli utenti di concedere alla tua app l'accesso in scrittura a un gruppo di file multimediali. Utilizza la
createWriteRequest()
come descritto nella sezione su come gestire gruppi di contenuti multimediali
file.
Se la tua app ha un altro caso d'uso non coperto dallo spazio di archiviazione limitato, invia una richiesta di funzionalità e disattiva temporaneamente lo spazio di archiviazione limitato.
Rimuovere una voce
Per rimuovere dal media store un elemento non più necessario per la tua app, utilizza la logica simile a quanto mostrato nello snippet di codice riportato di seguito:
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);
Se l'archiviazione con ambito non è disponibile o non è abilitata, puoi utilizzare la versione precedente
snippet di codice per rimuovere i file di proprietà di altre app. Tuttavia, se lo spazio di archiviazione limitato è abilitato, devi acquisire un RecoverableSecurityException
per ogni file che la tua app vuole rimuovere, come descritto nella sezione sull'aggiornamento degli elementi multimediali.
Se la tua app funziona su Android 11 o versioni successive, puoi consentire agli utenti
scegli un gruppo di file multimediali da rimuovere. Utilizza la createTrashRequest()
o il metodo
createDeleteRequest()
come descritto nella sezione su come gestire gruppi di contenuti multimediali
file.
Se la tua app ha un altro caso d'uso non coperto dall'archiviazione con ambito, invia un richiesta di funzionalità e disattivare temporaneamente l'ambito spazio di archiviazione.
Rileva gli aggiornamenti dei file multimediali
La tua app potrebbe dover identificare volumi di archiviazione contenenti file multimediali aggiunti o modificati dalle app rispetto a un momento precedente. Per rilevare queste variazioni
in modo più affidabile, passi il volume di archiviazione di interesse
getGeneration()
Finché la versione dell'archivio multimediale non cambia, il valore restituito di questo
aumenta monotonicamente nel tempo.
In particolare, getGeneration()
è più efficace delle date nelle colonne dei contenuti multimediali,
come
DATE_ADDED
e DATE_MODIFIED
.
Il motivo è che i valori della colonna multimediale possono cambiare quando un'app chiama
setLastModified()
o quando
l'utente cambia l'orologio di sistema.
Gestire gruppi di file multimediali
Su Android 11 e versioni successive, puoi chiedere all'utente di selezionare un gruppo di file multimediali, quindi aggiornali con una singola operazione. Questi offrono una migliore coerenza tra i dispositivi e i metodi semplificano per consentire agli utenti di gestire le raccolte di contenuti multimediali.
I metodi che forniscono questo "aggiornamento batch" includono seguenti:
createWriteRequest()
- Richiedi all'utente di concedere alla tua app l'accesso in scrittura al gruppo specificato di file multimediali.
createFavoriteRequest()
- Richiedi all'utente di contrassegnare i file multimediali specificati come propri "preferito" contenuti multimediali sul dispositivo. Qualsiasi app con accesso in lettura a questo file può vedrai che l'utente ha contrassegnato il file come "preferito".
createTrashRequest()
Richiedi all'utente di spostare i file multimediali specificati nel cestino del dispositivo. Gli elementi nel cestino vengono eliminati definitivamente dopo un periodo definito dal sistema punto.
createDeleteRequest()
Richiedi all'utente di eliminare definitivamente i file multimediali specificati immediatamente, senza metterli preventivamente nel cestino.
Dopo aver chiamato uno di questi metodi, il sistema crea una
PendingIntent
. Dopo l'attivazione dell'app
richiama questo intent, gli utenti vedono una finestra di dialogo che richiede il consenso per la tua app
per aggiornare o eliminare i file multimediali specificati.
Ad esempio, ecco come strutturare una chiamata a 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);
Valuta la risposta dell'utente. Se l'utente ha dato il consenso, procedi con il operazioni multimediali. In caso contrario, spiega all'utente perché la tua app richiede autorizzazione:
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. */ } } }
Puoi utilizzare lo stesso pattern generale con
createFavoriteRequest()
,
createTrashRequest()
,
e
createDeleteRequest()
.
Autorizzazione per la gestione dei contenuti multimediali
Gli utenti potrebbero considerare attendibile una determinata app per la gestione dei contenuti multimediali, ad esempio per apportare modifiche frequenti ai file multimediali. Se la tua app ha come target Android 11 o versioni successive e non è l'app Galleria predefinita del dispositivo, devi mostrare all'utente una finestra di dialogo di conferma ogni volta che la tua app tenta di modificare o eliminare un file.
Se la tua app ha come target Android 12 (livello API 31) o versioni successive, puoi richiedere Gli utenti concedono alla tua app l'accesso all'autorizzazione speciale gestione dei contenuti multimediali. Questo consente alla tua app di eseguire tutte le operazioni indicate di seguito senza dover chiedere l'utente per ogni operazione su file:
- Modificare i file utilizzando
createWriteRequest()
- Spostare file dentro e fuori dal cestino utilizzando
createTrashRequest()
- Elimina file utilizzando
createDeleteRequest()
Per farlo, segui questi passaggi:
Dichiara il Autorizzazione
MANAGE_MEDIA
e aiREAD_EXTERNAL_STORAGE
nel file manifest dell'app.Per chiamare il numero
createWriteRequest()
senza mostrare una conferma dichiara laACCESS_MEDIA_LOCATION
anche l'autorizzazione.Nell'app, mostra all'utente una UI per spiegare perché potrebbe voler concedere l'accesso per la gestione dei contenuti multimediali alla tua app.
Richiama l'azione dell'intent
ACTION_REQUEST_MANAGE_MEDIA
. Gli utenti vengono indirizzati alla schermata App di gestione dei contenuti multimediali in impostazioni di sistema. Da qui, gli utenti possono concedere l'accesso speciale all'app.
Casi d'uso che richiedono un'alternativa al media store
Se la tua app svolge principalmente uno dei seguenti ruoli, valuta la possibilità di
un'alternativa alle API MediaStore
.
Lavorare con altri tipi di file
Se la tua app funziona con documenti e file che non contengono esclusivamente contenuti multimediali
quali file che utilizzano l'estensione EPUB o PDF, utilizzano i
ACTION_OPEN_DOCUMENT
azione per intent, come descritto nella guida all'archiviazione
e l'accesso a documenti e altre
di archiviazione dei file.
Condivisione di file nelle app complementari
Se fornisci una suite di app complementari, ad esempio un'app di messaggistica e
un'app per profilo, configura la condivisione di file
utilizzando gli URI content://
. Consigliamo inoltre di eseguire questo flusso di lavoro come migliore
pratica.
Risorse aggiuntive
Per ulteriori informazioni su come archiviare e accedere ai contenuti multimediali, consulta quanto segue Google Cloud.
Campioni
- MediaStore disponibili su GitHub