Archiviazione esterna

L'archiviazione esterna si riferisce all'archiviazione file che non si trova nello spazio di archiviazione interno e non è accessibile esclusivamente all'app responsabile del file. Lo scopo principale dell'archiviazione esterna è quello di fornire una posizione in cui inserire i file che devono essere condivisi tra app o troppo grandi per adattarsi allo spazio di archiviazione interno.

Storicamente, l'archiviazione esterna si riferiva a una partizione del disco su supporti rimovibili, ad esempio una scheda SD (nota anche come archiviazione portabile). Questa distinzione non è più rilevante perché i dispositivi Android si sono evoluti e molti dispositivi Android non supportano più l'archiviazione rimovibile. Alcuni dispositivi allocheranno invece una parte della memoria interna non volatile ad Android che può eseguire la stessa funzione dei supporti rimovibili. Questa operazione è nota come spazio di archiviazione emulato ed è ancora considerato come spazio di archiviazione esterno. In alternativa, alcuni dispositivi Android possono avere più partizioni di archiviazione esterne. Ad esempio, un tablet Android (oltre al relativo spazio di archiviazione interno) potrebbe avere lo spazio di archiviazione emulato e uno o più slot per una scheda SD. Tutte queste partizioni vengono considerate da Android come risorsa di archiviazione esterna.

Nei dispositivi con più utenti, ogni utente avrà una directory dedicata nella partizione di archiviazione esterna primaria per l'archiviazione esterna. Le app in esecuzione come utente non avranno accesso ai file di un altro utente nel dispositivo. I file per tutti gli utenti sono ancora leggibili al mondo e scrivibili in tutto il mondo; Tuttavia, Android sandboxrà ogni profilo utente dagli altri.

La lettura e la scrittura nei file sono quasi identiche in Xamarin.Android, così come per qualsiasi altra applicazione .NET. L'app Xamarin.Android determina il percorso del file che verrà modificato, quindi usa idiomi .NET standard per l'accesso ai file. Poiché i percorsi effettivi dell'archiviazione interna ed esterna possono variare dal dispositivo al dispositivo o dalla versione android alla versione android, non è consigliabile impostare come hardcoded il percorso dei file. Xamarin.Android espone invece le API Android native che consentiranno di determinare il percorso dei file nell'archiviazione interna ed esterna.

Questa guida illustra i concetti e le API in Android specifici per l'archiviazione esterna.

File pubblici e privati nell'archiviazione esterna

Esistono due tipi diversi di file che un'app può mantenere nell'archiviazione esterna:

  • File privati : i file privati sono file specifici dell'applicazione (ma sono ancora leggibili e scrivibili in tutto il mondo). Android prevede che i file privati vengano archiviati in una directory specifica nell'archiviazione esterna. Anche se i file sono denominati "privati", sono ancora visibili e accessibili da altre app nel dispositivo, non sono autorizzati ad alcuna protezione speciale da Android.

  • File pubblici : si tratta di file non considerati specifici per l'applicazione e che devono essere condivisi liberamente.

Le differenze tra questi file sono principalmente concettuali. I file privati sono privati nel senso che sono considerati parte dell'applicazione, mentre i file pubblici sono tutti gli altri file presenti nell'archiviazione esterna. Android offre due API diverse per la risoluzione dei percorsi dei file privati e pubblici, ma in caso contrario le stesse API .NET vengono usate per leggere e scrivere in questi file. Queste sono le stesse API descritte nella sezione relativa alla lettura e alla scrittura.

File esterni privati

I file esterni privati sono considerati specifici di un'applicazione (simile ai file interni), ma vengono mantenuti in una risorsa di archiviazione esterna per un numero qualsiasi di motivi (ad esempio troppo grandi per l'archiviazione interna). Analogamente ai file interni, questi file verranno eliminati quando l'app viene disinstallata dall'utente.

Il percorso primario per i file esterni privati viene trovato chiamando il metodo Android.Content.Context.GetExternalFilesDir(string type). Questo metodo restituirà un Java.IO.File oggetto che rappresenta la directory di archiviazione esterna privata per l'app. Il passaggio null a questo metodo restituirà il percorso alla directory di archiviazione dell'utente per l'applicazione. Ad esempio, per un'applicazione con il nome com.companyname.appdel pacchetto , la directory "radice" dei file esterni privati sarà:

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

Questo documento farà riferimento alla directory di archiviazione per i file privati nell'archiviazione esterna come PRIVATE_EXTERNAL_STORAGE.

Il parametro per GetExternalFilesDir() è una stringa che specifica una directory dell'applicazione. Si tratta di una directory destinata a fornire un percorso standard per un'organizzazione logica di file. I valori stringa sono disponibili tramite costanti nella Android.OS.Environment classe :

Android.OS.Environment Directory
DirectoryAlarms PRIVATE_EXTERNAL_STORAGE/allarmi
DirectoryDcim PRIVATE_EXTERNAL_STORAGE/DCIM
DirectoryDownloads PRIVATE_EXTERNAL_STORAGE/Download
DirectoryDocuments PRIVATE_EXTERNAL_STORAGE/documenti
DirectoryMovies PRIVATE_EXTERNAL_STORAGE/film
Directory Musica PRIVATE_EXTERNAL_STORAGE/Musica
DirectoryNotifications PRIVATE_EXTERNAL_STORAGE/Notifiche
DirectoryPodcast PRIVATE_EXTERNAL_STORAGE/Podcast
DirectoryRingtones PRIVATE_EXTERNAL_STORAGE/suonerie
DirectoryPictures PRIVATE_EXTERNAL_STORAGE/immagini

Per i dispositivi con più partizioni di archiviazione esterne, ogni partizione avrà una directory destinata ai file privati. Il metodo Android.Content.Context.GetExternalFilesDirs(string type) restituirà una matrice di Java.IO.Files. Ogni oggetto rappresenterà una directory privata specifica dell'applicazione in tutti i dispositivi di archiviazione condivisi/esterni in cui l'applicazione può inserire i file di cui è proprietario.

Importante

Il percorso esatto della directory di archiviazione esterna privata può variare da dispositivo a dispositivo e tra le versioni di Android. Per questo motivo, le app non devono impostare come hardcoded il percorso di questa directory e usare invece le API Xamarin.Android, ad esempio Android.Content.Context.GetExternalFilesDir().

File esterni pubblici

I file pubblici sono file presenti in una risorsa di archiviazione esterna non archiviata nella directory allocata da Android per i file privati. I file pubblici non verranno eliminati quando l'app viene disinstallata. Le app Android devono essere concesse l'autorizzazione prima di poter leggere o scrivere file pubblici. È possibile che i file pubblici esistano ovunque nell'archiviazione esterna, ma per convenzione Android prevede che i file pubblici esistano nella directory identificata dalla proprietà Android.OS.Environment.ExternalStorageDirectory. Questa proprietà restituirà un Java.IO.File oggetto che rappresenta la directory di archiviazione esterna primaria. Ad esempio, Android.OS.Environment.ExternalStorageDirectory può fare riferimento alla directory seguente:

/storage/emulated/0/

Questo documento farà riferimento alla directory di archiviazione per i file pubblici nell'archiviazione esterna come PUBLIC_EXTERNAL_STORAGE.

Android supporta anche il concetto di directory dell'applicazione in PUBLIC_EXTERNAL_STORAGE. Queste directory corrispondono esattamente alle directory dell'applicazione per PRIVATE_EXTERNAL_STORAGE e sono descritte nella tabella nella sezione precedente. Il metodo Android.OS.Environment.GetExternalStoragePublicDirectory(string directoryType) restituirà un Java.IO.File oggetto che corrisponde a una directory dell'applicazione pubblica. Il directoryType parametro è un parametro obbligatorio e non può essere null.

Ad esempio, la chiamata Environment.GetExternalStoragePublicDirectory(Environment.DirectoryDocuments).AbsolutePath restituirà una stringa simile alla seguente:

/storage/emulated/0/Documents

Importante

Il percorso esatto della directory di archiviazione esterna pubblica può variare da dispositivo a dispositivo e tra le versioni di Android. Per questo motivo, le app non devono impostare come hardcoded il percorso di questa directory e usare invece le API Xamarin.Android, ad esempio Android.OS.Environment.ExternalStorageDirectory.

Uso dell'archiviazione esterna

Dopo che un'app Xamarin.Android ha ottenuto il percorso completo di un file, deve usare qualsiasi API .NET standard per la creazione, la lettura, la scrittura o l'eliminazione di file. In questo modo viene ingrandita la quantità di codice compatibile con più piattaforme per un'app. Tuttavia, prima di tentare di accedere a un file di un'app Xamarin.Android, è necessario assicurarsi che sia possibile accedere a tale file.

  1. Verificare l'archiviazione esterna: a seconda della natura dell'archiviazione esterna, è possibile che non sia montata e utilizzabile dall'app. Tutte le app devono controllare lo stato della risorsa di archiviazione esterna prima di tentare di usarla.
  2. Eseguire un controllo delle autorizzazioni di runtime: un'app Android deve richiedere l'autorizzazione dell'utente per accedere all'archiviazione esterna. Ciò significa che è necessario eseguire una richiesta di autorizzazione di runtime prima di qualsiasi accesso ai file. La guida Autorizzazioni in Xamarin.Android contiene altri dettagli sulle autorizzazioni android.

Ognuna di queste due attività verrà discussa di seguito.

Verifica che l'archiviazione esterna sia disponibile

Il primo passaggio prima della scrittura nell'archiviazione esterna consiste nel verificare che sia leggibile o scrivibile. La Android.OS.Environment.ExternalStorageState proprietà contiene una stringa che identifica lo stato dell'archiviazione esterna. Questa proprietà restituirà una stringa che rappresenta lo stato. Questa tabella è un elenco dei ExternalStorageState valori che potrebbero essere restituiti da Environment.ExternalStorageState:

External Archiviazione State Descrizione
MediaBadRemoval Il supporto è stato rimosso bruscamente senza essere correttamente smontato.
Controllo multimediale Il supporto è presente ma è in fase di controllo del disco.
MediaEjecting Il supporto è in corso di smontaggio ed espulso.
MediaMounted I supporti sono montati e possono essere letti o scritti in.
MediaMountedReadOnly Il supporto è montato ma può essere letto solo da.
MediaNofs Il supporto è presente ma non contiene un file system adatto per Android.
MediaRemoved Non è presente alcun supporto.
MediaShared Il supporto è presente, ma non è montato. Viene condiviso tramite USB con un altro dispositivo.
MediaUnknown Lo stato dei supporti non è riconosciuto da Android.
MediaUnmountable Il supporto è presente ma non può essere montato da Android.
MediaUnmounted Il supporto è presente ma non è montato.

La maggior parte delle app Android dovrà controllare solo se è montata l'archiviazione esterna. Il frammento di codice seguente mostra come verificare che l'archiviazione esterna sia montata per l'accesso in sola lettura o in lettura/scrittura:

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

Autorizzazioni di archiviazione esterna

Android considera l'accesso all'archiviazione esterna un'autorizzazione pericolosa, che in genere richiede all'utente di concedere l'autorizzazione per accedere alla risorsa. L'utente può revocare questa autorizzazione in qualsiasi momento. Ciò significa che è necessario eseguire una richiesta di autorizzazione di runtime prima di qualsiasi accesso ai file. Alle app vengono concesse automaticamente le autorizzazioni per leggere e scrivere i propri file privati. È possibile che le app leggano e scrivono i file privati appartenenti ad altre app dopo che l'utente ha concesso l'autorizzazione .

Tutte le app Android devono dichiarare una delle due autorizzazioni per l'archiviazione esterna nel AndroidManifest.xml . Per identificare le autorizzazioni, è necessario aggiungere uno dei due uses-permission elementi seguenti a AndroidManifest.xml:

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

Nota

Se l'utente concede WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE viene concesso anche in modo implicito. Non è necessario richiedere entrambe le autorizzazioni in AndroidManifest.xml.

È anche possibile aggiungere le autorizzazioni usando la scheda Manifesto Android delle proprietà della soluzione:

Solution Explorer - Required Permissions for Visual Studio

In generale, tutte le autorizzazioni pericolose devono essere approvate dall'utente. Le autorizzazioni per l'archiviazione esterna sono un'anomalia in quanto esistono eccezioni a questa regola, a seconda della versione di Android in cui è in esecuzione l'app:

Flowchart of external storage permission checks

Per altre informazioni sull'esecuzione di richieste di autorizzazione di runtime, vedere la guida Autorizzazioni in Xamarin.Android. LocalFiles monodroid-sampleillustra anche un modo per eseguire i controlli delle autorizzazioni di runtime.

Concessione e revoca delle autorizzazioni con ADB

Nel corso dello sviluppo di un'app Android, potrebbe essere necessario concedere e revocare le autorizzazioni per testare i vari flussi di lavoro coinvolti nei controlli delle autorizzazioni di runtime. È possibile eseguire questa operazione al prompt dei comandi usando ADB. I frammenti di riga di comando seguenti illustrano come concedere o revocare le autorizzazioni usando ADB per un'app Android il cui nome del pacchetto è com.companyname.app:

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

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

Eliminazione di file

Qualsiasi API C# standard può essere usata per eliminare un file dall'archiviazione esterna, ad esempio System.IO.File.Delete. È anche possibile usare le API Java a scapito della portabilità del codice. Ad esempio:

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