Casi d'uso e best practice per lo spazio di archiviazione di Android

Per offrire agli utenti un maggiore controllo sui file e limitare il disordine dei file, Android 10 ha introdotto un nuovo paradigma di archiviazione per le app chiamato archiviazione con ambito. L'archiviazione mirata cambia il modo in cui le app archiviano i file e accedono ai file sulla memoria esterna di un dispositivo. Per aiutarti a eseguire la migrazione della tua app in modo che supporti lo spazio di archiviazione con ambito, segui le best practice per casi d'uso di archiviazione comuni descritti in questa guida. I casi d'uso sono suddivisi in due categorie: gestione dei file multimediali e gestione dei file non multimediali.

Per scoprire di più su come archiviare e accedere ai file su Android, consulta le guide di formazione sullo spazio di archiviazione.

Gestire i file multimediali

Questa sezione descrive alcuni dei casi d'uso comuni per la gestione di file multimediali (file video, immagine e audio) e spiega l'approccio generale che può essere utilizzato dalla tua app. La seguente tabella riassume ciascuno di questi casi d'uso e fornisce i link a ciascuna sezione contenente ulteriori dettagli.

Caso d'uso Riepilogo
Mostrare tutti i file immagine o video Usa lo stesso approccio per tutte le versioni di Android.
Mostrare immagini o video da una determinata cartella Usa lo stesso approccio per tutte le versioni di Android.
Accedere alle informazioni sulla posizione dalle foto Utilizza un approccio se la tua app utilizza l'archiviazione con ambito. Utilizza un approccio diverso se la tua app disattiva l'archiviazione mirata.
Definire la posizione di archiviazione per i nuovi download Utilizza un approccio se la tua app utilizza l'archiviazione con ambito. Utilizza un approccio diverso se la tua app disattiva l'archiviazione mirata.
Esportare i file multimediali degli utenti su un dispositivo Usa lo stesso approccio per tutte le versioni di Android.
Modifica o eliminazione di più file multimediali in un'unica operazione Usa un unico approccio per Android 11. Per Android 10, disattiva l'archiviazione mirata e utilizza l'approccio per Android 9 e versioni precedenti.
Importa una singola immagine già esistente Usa lo stesso approccio per tutte le versioni di Android.
Acquisire una singola immagine Usa lo stesso approccio per tutte le versioni di Android.
Condividere file multimediali con altre app Usa lo stesso approccio per tutte le versioni di Android.
Condividere file multimediali con un'app specifica Usa lo stesso approccio per tutte le versioni di Android.
Accedi ai file da codice o librerie che utilizzano percorsi diretti dei file Usa un unico approccio per Android 11. Per Android 10, disattiva l'archiviazione mirata e utilizza l'approccio per Android 9 e versioni precedenti.

Mostra i file di immagini o video da più cartelle

Esegui una query su una raccolta multimediale utilizzando l'API query(). Per filtrare o ordinare i file multimediali, regola i parametri projection, selection, selectionArgs e sortOrder.

Mostrare le immagini o i video di una determinata cartella

Usa questo approccio:

  1. Segui le best practice descritte in Richiedere le autorizzazioni app, richiedere l'autorizzazione READ_EXTERNAL_STORAGE.
  2. Recupera i file multimediali in base al valore di MediaColumns.DATA, che contiene il percorso del file system assoluto dell'elemento multimediale sul disco.

Nota: quando accedi a un file multimediale esistente, puoi utilizzare il valore della colonna DATA nella logica. Il motivo è che questo valore ha un percorso file valido. Tuttavia, non dare per scontato che il file sia sempre disponibile. Preparati a gestire eventuali errori di I/O basati su file che potrebbero verificarsi.

Per creare o aggiornare un file multimediale, invece, non utilizzare la colonna DATA. Utilizza invece le colonne DISPLAY_NAME e RELATIVE_PATH.

Accedere alle informazioni sulla posizione dalle foto

Se l'app utilizza lo spazio di archiviazione limitato, segui i passaggi nella sezione Informazioni sulla posizione nelle fotografie della guida allo spazio di archiviazione per i contenuti multimediali.

Definisci la posizione di archiviazione per i nuovi download

Se l'app utilizza lo spazio di archiviazione limitato, presta attenzione alla posizione in cui scegli di archiviare i file multimediali che scarichi.

Se altre app richiedono l'accesso ai file, valuta la possibilità di utilizzare raccolte multimediali ben definite per i download o le raccolte di documenti.

Su Android 11 e versioni successive, i file all'interno della directory esterna specifica per l'app non sono accessibili ad altre app, anche se utilizzi DownloadManager per recuperarli.

Esportare i file multimediali dell'utente su un dispositivo

Definisci una posizione predefinita appropriata in cui archiviare i file multimediali degli utenti:

Modifica o elimina più file multimediali in un'unica operazione

Incorpora una logica basata sulle versioni di Android su cui viene eseguita la tua app.

In esecuzione su Android 11

Usa questo approccio:

  1. Crea un intent in attesa per la richiesta di scrittura o eliminazione della tua app utilizzando MediaStore.createWriteRequest() o MediaStore.createTrashRequest(), quindi chiedi all'utente l'autorizzazione per modificare un insieme di file richiamando quell'intent.
  2. Valuta la risposta dell'utente:

    • Se l'autorizzazione è stata concessa, procedi con l'operazione di modifica o eliminazione.
    • Se l'autorizzazione non è stata concessa, spiega all'utente perché la funzionalità nella tua app richiede l'autorizzazione.

Scopri di più su come gestire gruppi di file multimediali utilizzando questi metodi disponibili su Android 11 e versioni successive.

In esecuzione su Android 10

Se la tua app ha come target Android 10 (livello API 29), disattiva lo spazio di archiviazione basato sull'ambito e continua a utilizzare l'approccio per Android 9 e versioni precedenti per eseguire questa operazione.

Android 9 o versioni precedenti

Usa questo approccio:

  1. Segui le best practice descritte in Richiedere le autorizzazioni app, richiedere l'autorizzazione WRITE_EXTERNAL_STORAGE.
  2. Utilizza l'API MediaStore per modificare o eliminare i file multimediali.

Importa una singola immagine già esistente

Se vuoi importare una singola immagine già esistente (ad esempio, da utilizzare come foto per il profilo di un utente), l'app può utilizzare la propria UI per l'operazione oppure il selettore di sistema.

Presenta la tua interfaccia utente

Usa questo approccio:

  1. Segui le best practice descritte in Richiedere le autorizzazioni app, richiedere l'autorizzazione READ_EXTERNAL_STORAGE.
  2. Utilizza l'API query() per eseguire una query su una raccolta di contenuti multimediali.
  3. Visualizza i risultati nell'UI personalizzata della tua app.

Utilizzare il selettore di sistema

Utilizza l'intent ACTION_GET_CONTENT, che chiede all'utente di scegliere un'immagine da importare.

Se vuoi filtrare i tipi di immagini che il selettore di sistema presenta all'utente tra cui scegliere, puoi utilizzare setType() o EXTRA_MIME_TYPES.

Acquisisci una singola immagine

Quando vuoi acquisire una singola immagine da utilizzare nella tua app (ad esempio, da usare come foto del profilo di un utente), utilizza l'intent ACTION_IMAGE_CAPTURE per chiedere all'utente di scattare una foto con la fotocamera del dispositivo. Il sistema memorizza la foto acquisita nella tabella MediaStore.Images.

Condividere file multimediali con altre app

Utilizza il metodo insert() per aggiungere record direttamente in MediaStore. Per ulteriori informazioni, consulta la sezione Aggiungere un elemento della guida all'archiviazione per i contenuti multimediali.

Condividere file multimediali con un'app specifica

Usa il componente FileProvider di Android, come descritto nella guida Impostazione della condivisione file.

Accedi ai file da codice o librerie che utilizzano percorsi diretti dei file

Incorpora una logica basata sulle versioni di Android su cui viene eseguita la tua app.

In esecuzione su Android 11

Usa questo approccio:

  1. Segui le best practice descritte in Richiedere le autorizzazioni app, richiedere l'autorizzazione READ_EXTERNAL_STORAGE.
  2. Accedi ai file utilizzando percorsi diretti dei file.

Per ulteriori informazioni, consulta la sezione su come aprire i file multimediali utilizzando i percorsi file diretti.

In esecuzione su Android 10

Se la tua app ha come target Android 10 (livello API 29), disattiva lo spazio di archiviazione basato sull'ambito e continua a utilizzare l'approccio per Android 9 e versioni precedenti per eseguire questa operazione.

Android 9 o versioni precedenti

Usa questo approccio:

  1. Segui le best practice descritte in Richiedere le autorizzazioni app, richiedere l'autorizzazione WRITE_EXTERNAL_STORAGE.
  2. Accedi ai file utilizzando percorsi diretti dei file.

Gestire i file non multimediali

Questa sezione descrive alcuni dei casi d'uso comuni per la gestione di file non multimediali e spiega l'approccio generale che può essere utilizzato dalla tua app. La seguente tabella riassume ciascuno di questi casi d'uso e rimanda a ciascuna sezione che contiene ulteriori dettagli.

Caso d'uso Riepilogo
Aprire un file di documento Usa lo stesso approccio per tutte le versioni di Android.
Scrittura su file su volumi di archiviazione secondari Usa un unico approccio per Android 11. Utilizza un approccio diverso per le versioni precedenti di Android.
Eseguire la migrazione dei file esistenti da una posizione di archiviazione legacy Se possibile, esegui la migrazione dei file allo spazio di archiviazione con ambito. Disattiva l'archiviazione mirata per Android 10 quando necessario.
Condividere contenuti con altre app Usa lo stesso approccio per tutte le versioni di Android.
Memorizzare nella cache i file non multimediali Usa lo stesso approccio per tutte le versioni di Android.
Esportare file non multimediali su un dispositivo Utilizza un approccio se la tua app utilizza l'archiviazione con ambito. Utilizza un approccio diverso se la tua app disattiva l'archiviazione mirata.

Aprire un file di documento

Utilizza l'intent ACTION_OPEN_DOCUMENT per chiedere all'utente di scegliere un file da aprire utilizzando il selettore di sistema. Se vuoi filtrare i tipi di file che il selettore di sistema presenterà all'utente tra cui scegliere, puoi utilizzare setType() o EXTRA_MIME_TYPES.

Ad esempio, puoi trovare tutti i file PDF, ODT e TXT utilizzando il seguente codice:

Kotlin

startActivityForResult(
        Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
            addCategory(Intent.CATEGORY_OPENABLE)
            type = "*/*"
            putExtra(Intent.EXTRA_MIME_TYPES, arrayOf(
                    "application/pdf", // .pdf
                    "application/vnd.oasis.opendocument.text", // .odt
                    "text/plain" // .txt
            ))
        },
        REQUEST_CODE
      )

Java

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {
                "application/pdf", // .pdf
                "application/vnd.oasis.opendocument.text", // .odt
                "text/plain" // .txt
        });
        startActivityForResult(intent, REQUEST_CODE);

Scrittura in file su volumi di archiviazione secondari

I volumi di archiviazione secondari includono le schede SD. Puoi accedere alle informazioni su un determinato volume di archiviazione utilizzando la classe StorageVolume.

Incorpora una logica basata sulla versione di Android su cui viene eseguita l'app.

In esecuzione su Android 11

Usa questo approccio:

  1. Utilizza il modello di archiviazione con ambito.
  2. Scegliere come target Android 10 (livello API 29) o versioni precedenti.
  3. Dichiara l'autorizzazione WRITE_EXTERNAL_STORAGE.
  4. Esegui uno dei seguenti tipi di accesso:
    • Accesso ai file mediante l'API MediaStore.
    • Accesso diretto al percorso file utilizzando API come File o fopen().

In esecuzione su versioni precedenti

Utilizza Storage Access Framework, che consente agli utenti di selezionare la posizione su un volume di archiviazione secondario in cui la tua app può scrivere il file.

Esegui la migrazione dei file esistenti da una posizione di archiviazione legacy

Una directory è considerata una posizione di archiviazione legacy se non si tratta di una directory specifica dell'app o di una directory condivisa pubblica. Se la tua app crea o consuma file in una posizione di archiviazione legacy, ti consigliamo di eseguire la migrazione dei file dell'app in posizioni accessibili con spazio di archiviazione con ambito e di apportare eventuali modifiche necessarie all'app in modo che funzionino con i file nello spazio di archiviazione con ambito.

Mantieni l'accesso alla posizione di archiviazione legacy per la migrazione dei dati

La tua app deve mantenere l'accesso alla posizione di archiviazione legacy per eseguire la migrazione di qualsiasi file dell'app in posizioni accessibili con spazio di archiviazione con ambito. L'approccio da utilizzare dipende dal livello API target della tua app.

Se la tua app ha come target Android 11
  1. Imposta il flag preserveLegacyExternalStorage su true per conservare il modello di archiviazione legacy in modo che l'app possa eseguire la migrazione dei dati di un utente quando esegue l'upgrade alla nuova versione dell'app che ha come target Android 11.

  2. Continua a disattivare l'archiviazione basata sull'ambito in modo che la tua app possa continuare ad accedere ai tuoi file nella posizione di archiviazione precedente sui dispositivi Android 10.

Se la tua app ha come target Android 10

Disattiva l'archiviazione mirata per mantenere più semplice il comportamento della tua app nelle versioni di Android.

Esegui la migrazione dei dati dell'app

Quando la tua app è pronta per la migrazione, utilizza il seguente approccio:

  1. Scegli come target Android 10 o versioni precedenti.
  2. Disattiva l'archiviazione mirata in modo che la tua app abbia accesso ai file di cui devi eseguire la migrazione.
  3. Esegui il deployment di codice che utilizza l'API File per spostare i file dalla posizione attuale in /sdcard/ a una posizione accessibile con archiviazione limitata:

    1. Sposta tutti i file privati dell'app nella directory restituita dal metodo getExternalFilesDir().
    2. Sposta tutti i file non multimediali condivisi in una sottodirectory dedicata all'app della directory Downloads/.
  4. Rimuovi le directory di archiviazione legacy della tua app dalla directory /sdcard/.

Dopo aver installato la nuova versione dell'app, gli utenti completano il processo di migrazione dei dati sui propri dispositivi. Puoi monitorare il processo di migrazione della tua base utenti creando un evento di analisi.

Dopo che gli utenti hanno eseguito la migrazione dei loro dati, pubblica un altro aggiornamento nella tua app, che hai come target Android 11.

Condividere contenuti con altre app

Per condividere i file della tua app con una sola app, utilizza un FileProvider. Per le app che devono condividere file tra loro, ti consigliamo di utilizzare un fornitore di contenuti per ciascuna app, quindi di sincronizzare i dati man mano che le app vengono aggiunte alla raccolta.

Memorizza nella cache i file non multimediali

L'approccio da utilizzare dipende dal tipo di file che devi memorizzare nella cache.

Esportare file non multimediali su un dispositivo

Definisci una posizione predefinita appropriata in cui archiviare i file non multimediali. Consenti agli utenti di esportare file da directory specifiche dell'app a una posizione più generalmente accessibile. Utilizza i download o le raccolte di documenti di MediaStore per esportare file non multimediali nel dispositivo.

Disattivare temporaneamente lo spazio di archiviazione con ambito

Prima che la tua app sia completamente compatibile con lo spazio di archiviazione con ambito, puoi disattivarla temporaneamente sia nei test sia nell'app di produzione.

Disattiva i test

Su Android 10 (livello API 29) e versioni successive, i test dell'app vengono eseguiti per impostazione predefinita in una sandbox di archiviazione. Questa sandbox impedisce all'app di accedere ai file all'esterno della directory specifica dell'app e delle directory condivise pubblicamente.

Se un test restituisce dei file per l'host, ad esempio screenshot, dati di debug, dati di copertura o metriche sulle prestazioni, puoi scrivere questi file in directory globali. A questo scopo, aggiungi il flag seguente al cablaggio pertinente che rivoca am instrument:

-e no-isolated-storage 1

Questo flag influisce su tutti i comportamenti dello scenario di test con strumentazione e interessa tutto il codice di test richiamato. Di conseguenza, quando utilizzi questo flag, non puoi convalidare la compatibilità della tua app con lo spazio di archiviazione con ambito. Per l'output di test, è preferibile scrivere in uno spazio di archiviazione basato sulle app, leggibile dalla shell. Puoi quindi eseguire il pull di quella directory con ambito a livello di app. Per determinare da quale directory eseguire il pull, chiama getExternalMediaDirs().

Disattivare nell'app di produzione

Se la tua app ha come target Android 10 (livello API 29) o versioni precedenti, puoi disattivare temporaneamente l'archiviazione con ambito nell'app di produzione. Tuttavia, se scegli come target Android 10, devi impostare il valore di requestLegacyExternalStorage su true nel file manifest della tua app:

<manifest ... >
  <!-- This attribute is "false" by default on apps targeting
       Android 10. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

Per verificare il comportamento di un'app che ha come target Android 10 o versioni precedenti quando utilizzi lo spazio di archiviazione con ambito, puoi attivare questo comportamento impostando il valore requestLegacyExternalStorage su false. Se stai eseguendo test su un dispositivo che esegue Android 11, puoi anche utilizzare i flag di compatibilità delle app per verificare il comportamento dell'app con o senza spazio di archiviazione limitato.

Risorse aggiuntive

Per ulteriori informazioni sullo spazio di archiviazione di Android, consulta i seguenti materiali:

Post sui blog