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

Per offrire agli utenti un maggiore controllo sui propri file e limitare il disordine dei file, Android 10 ha introdotto un nuovo paradigma di archiviazione per le app chiamato archiviazione isolata. Le modifiche all'archiviazione isolata cambiano il modo in cui le app archiviano e accedono ai file nell'archiviazione esterna di un dispositivo. Per aiutarti a migrare la tua app per supportare lo spazio di archiviazione isolato, segui le best practice per i casi d'uso dello spazio di archiviazione comuni descritti in questa guida. I casi d'uso sono organizzati in due categorie: gestione dei file multimediali e gestione dei file non multimediali.

In molti casi, la tua app crea file a cui altre app non devono accedere o non dovrebbero accedere. Il sistema fornisce posizioni di archiviazione specifiche per le app per gestire questi file.

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

Gestire i file multimediali

Questa sezione descrive alcuni casi d'uso comuni per la gestione dei file multimediali (file video, immagine e audio) e spiega l'approccio di alto livello che la tua app può utilizzare. La tabella seguente riassume ciascuno di questi casi d'uso e fornisce link a ciascuna delle sezioni che contengono ulteriori dettagli.

Storytelling Riepilogo
Mostra tutti i file di immagini o video Utilizza lo stesso approccio per tutte le versioni di Android.
Mostrare immagini o video di una determinata cartella Utilizza lo stesso approccio per tutte le versioni di Android.
Accedere ai dati sulla posizione dalle foto Utilizza un approccio se la tua app utilizza l'archiviazione mirata. 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 mirata. Utilizza un approccio diverso se la tua app disattiva l'archiviazione mirata.
Esportare i file multimediali dell'utente su un dispositivo Utilizza lo stesso approccio per tutte le versioni di Android.
Modificare o eliminare più file multimediali in una singola operazione Utilizza un approccio per Android 11. Per Android 10, disattiva lo spazio di archiviazione isolato e utilizza l'approccio per Android 9 e versioni precedenti.
Importare una singola immagine già esistente Utilizza lo stesso approccio per tutte le versioni di Android.
Acquisire una singola immagine Utilizza lo stesso approccio per tutte le versioni di Android.
Condividere file multimediali con altre app Utilizza lo stesso approccio per tutte le versioni di Android.
Condividere file multimediali con un'app specifica Utilizza lo stesso approccio per tutte le versioni di Android.
Accedere ai file da codice o librerie che utilizzano percorsi di file diretti Utilizza un approccio per Android 11. Per Android 10, disattiva lo spazio di archiviazione isolato e utilizza l'approccio per Android 9 e versioni precedenti.

Mostra file di immagini o video di più cartelle

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

Mostrare immagini o video di una determinata cartella

Utilizza questo approccio:

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

Nota:quando accedi a un file multimediale esistente, puoi utilizzare il valore della colonna DATA nella tua logica. Questo perché questo valore ha un percorso del 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 la tua app utilizza lo spazio di archiviazione isolato, segui i passaggi descritti nella sezione Informazioni sulla posizione nelle fotografie della guida allo spazio di archiviazione dei contenuti multimediali.

Definisci la posizione di archiviazione per i nuovi download

Se la tua app utilizza l'archiviazione con ambito, fai 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 specifica dell'app esterna 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 per archiviare i file multimediali degli utenti:

Modificare o eliminare più file multimediali in un'unica operazione

Incorpora la logica in base alle versioni di Android su cui viene eseguita la tua app.

In esecuzione su Android 11

Utilizza questo approccio:

  1. Crea un intent in attesa per la richiesta di scrittura o eliminazione della tua app utilizzando MediaStore.createWriteRequest() o MediaStore.createTrashRequest() e poi chiedi all'utente l'autorizzazione per modificare un insieme di file richiamando l'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à della tua app ha bisogno dell'autorizzazione.

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

Con Android 10

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

Eseguire Android 9 o versioni precedenti

Utilizza questo approccio:

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

Importare una singola immagine già esistente

Quando vuoi importare una singola immagine esistente (ad esempio, per utilizzarla come foto per il profilo di un utente), la tua app può utilizzare la propria UI per l'operazione o il selettore di sistema.

Presentare la propria interfaccia utente

Utilizza questo approccio:

  1. Seguendo le best practice descritte in Richiedere le autorizzazioni dell'app, richiedi l'autorizzazione READ_EXTERNAL_STORAGE.
  2. Utilizza l'API query() per interrogare una raccolta di contenuti multimediali.
  3. Visualizza i risultati nell'interfaccia utente 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.

Acquisire una singola immagine

Quando vuoi acquisire una singola immagine da utilizzare nella tua app (ad esempio, per utilizzarla come foto per il profilo di un utente), utilizza l'intent ACTION_IMAGE_CAPTURE per chiedere all'utente di scattare una foto utilizzando 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 maggiori informazioni, consulta la sezione Aggiungere un elemento della guida allo spazio di archiviazione dei contenuti multimediali.

Condividere file multimediali con un'app specifica

Utilizza il componente Android FileProvider, come descritto nella guida Configurazione della condivisione di file.

Accedere ai file da codice o librerie che utilizzano percorsi di file diretti

Incorpora la logica in base alle versioni di Android su cui viene eseguita la tua app.

In esecuzione su Android 11

Utilizza questo approccio:

  1. Seguendo le best practice descritte in Richiedere le autorizzazioni dell'app, richiedi l'autorizzazione READ_EXTERNAL_STORAGE.
  2. Accedi ai file utilizzando i percorsi diretti.

Per saperne di più, consulta la sezione su come aprire i file multimediali utilizzando percorsi di file diretti.

Con Android 10

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

Eseguire Android 9 o versioni precedenti

Utilizza questo approccio:

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

Gestire i file non multimediali

Questa sezione descrive alcuni casi d'uso comuni per la gestione di file non multimediali e spiega l'approccio di alto livello che la tua app può utilizzare. La tabella seguente riepiloga ciascuno di questi casi d'uso e rimanda alle sezioni che contengono ulteriori dettagli.

Storytelling Riepilogo
Aprire un file di documento Utilizza lo stesso approccio per tutte le versioni di Android.
Scrivere nei file sui volumi di archiviazione secondari Utilizza un 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 precedente Esegui la migrazione dei file allo spazio di archiviazione isolato, se possibile. Disattiva l'archiviazione con ambito per Android 10, se necessario.
Condividere contenuti con altre app Utilizza lo stesso approccio per tutte le versioni di Android.
Memorizzare nella cache i file non multimediali Utilizza 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 mirata. 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);

Scrivere nei file sui volumi di archiviazione secondari

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

Incorpora la logica in base alla versione di Android su cui viene eseguita la tua app.

Funziona su Android 11

Utilizza questo approccio:

  1. Utilizza il modello di archiviazione con ambito.
  2. Avere 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 tramite l'API MediaStore.
    • Accesso diretto al percorso dei file tramite API come File o fopen().

Esecuzione su versioni precedenti

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

Eseguire la migrazione dei file esistenti da una posizione di archiviazione precedente

Una directory viene considerata una posizione di archiviazione legacy se non è una directory specifica dell'app o una directory condivisa pubblica. Se la tua app crea o utilizza file in una posizione di archiviazione precedente, ti consigliamo di eseguire la migrazione dei file dell'app in posizioni accessibili con l'archiviazione isolata e apportare le modifiche necessarie all'app per utilizzare i file nell'archiviazione isolata.

Mantenere 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 migrare i file dell'app in posizioni accessibili con lo spazio di archiviazione isolato. 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 precedente in modo che la tua app possa eseguire la migrazione dei dati di un utente quando esegue l'upgrade alla nuova versione della tua app che ha come target Android 11.

  2. Continua a disattivare lo spazio di archiviazione isolato 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 isolata per semplificare la manutenzione del comportamento della tua app nelle varie versioni di Android.

Eseguire la migrazione dei dati delle app

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

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

    1. Sposta tutti i file privati dell'app nella directory restituita dal metodo getExternalFilesDir().
    2. Sposta tutti i file condivisi non multimediali 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 nella tua base utenti creando un evento Analytics.

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

Condividere contenuti con altre app

Per condividere i file della tua app con un'altra app, utilizza un FileProvider. Per le app che devono condividere file tra loro, consigliamo di utilizzare un content provider per ogni app e poi sincronizzare i dati man mano che le app vengono aggiunte alla raccolta.

Memorizzare 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 per archiviare i file non multimediali. Consenti agli utenti di esportare i file dalle directory specifiche delle app in una posizione più accessibile. Utilizza i download o le raccolte di documenti di MediaStore per esportare i file non multimediali sul dispositivo.

Gestire i file specifici per app

Se la tua app crea file a cui altre app non devono accedere o non devono accedere, puoi archiviarli in posizioni di archiviazione specifiche dell'app.

Directory di archiviazione interna

Il sistema impedisce ad altre app di accedere a queste posizioni e su Android 10 (livello API 29) e versioni successive, queste posizioni sono criptate. Queste posizioni sono un buon posto per archiviare dati sensibili a cui può accedere solo la tua app.

Directory di archiviazione esterna

Se la memoria interna non offre spazio sufficiente per archiviare i file specifici dell'app, valuta la possibilità di utilizzare una memoria esterna. Sebbene sia possibile che un'altra app acceda a queste directory se dispone delle autorizzazioni appropriate, i file archiviati in queste directory sono destinati all'uso esclusivo da parte della tua app.

Su Android 4.4 (livello API 19) o versioni successive, la tua app non deve richiedere autorizzazioni relative all'archiviazione per accedere alle directory specifiche dell'app all'interno dell'archiviazione esterna.

Quando l'utente disinstalla la tua app, i file salvati nello spazio di archiviazione specifico dell'app vengono rimossi, pertanto non devi utilizzare questo spazio di archiviazione per salvare qualsiasi elemento che l'utente si aspetta di conservare indipendentemente dalla tua app.

Disattivare temporaneamente l'archiviazione isolata

Prima che la tua app sia completamente compatibile con l'archiviazione isolata, puoi disattivare temporaneamente l'opzione sia nei test sia nell'app di produzione.

Disattivare l'opzione nei test

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

Se un test genera file per l'host, ad esempio screenshot, dati di debug, dati di copertura o metriche di rendimento, puoi scrivere questi file nelle directory globali. Per farlo, aggiungi il seguente flag all'harness pertinente che richiama am instrument:

-e no-isolated-storage 1

Questo flag influisce su tutto il comportamento dello scenario di test strumentato e su tutto il codice di test richiamato. Pertanto, quando utilizzi questo flag, non puoi convalidare la compatibilità della tua app con lo spazio di archiviazione isolato. Per l'output di test, è meglio scrivere in uno spazio di archiviazione con ambito app leggibile dalla shell. A questo punto puoi estrarre la directory con ambito app. Per determinare da quale directory estrarre i dati, chiama getExternalMediaDirs().

Disattivare l'opzione nell'app di produzione

Se la tua app ha come target Android 10 (livello API 29) o versioni precedenti, puoi disattivare temporaneamente lo spazio di archiviazione isolato nella tua app di produzione. Se hai come target Android 10, tuttavia, devi impostare il valore di requestLegacyExternalStorage su true nel file manifest dell'app:

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

Per testare il comportamento di un'app che ha come target Android 10 o versioni precedenti quando utilizza lo spazio di archiviazione isolato, puoi attivare il comportamento impostando il valore di requestLegacyExternalStorage su false. Se esegui test su un dispositivo con Android 11, puoi anche utilizzare i flag di compatibilità delle app per testare il comportamento dell'app con o senza lo spazio di archiviazione isolato.

Risorse aggiuntive

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

Post del blog