Armazenamento externo
O armazenamento externo refere-se ao armazenamento de arquivos que não está no armazenamento interno e não é acessível exclusivamente ao aplicativo responsável pelo arquivo. O objetivo principal do armazenamento externo é fornecer um local para colocar arquivos que devem ser compartilhados entre aplicativos ou que são muito grandes para caber no armazenamento interno.
Historicamente falando, o armazenamento externo se referia a uma partição de disco em mídia removível, como um cartão SD (também conhecido como armazenamento portátil). Essa distinção não é mais tão relevante quanto os dispositivos Android evoluíram e muitos dispositivos Android não suportam mais armazenamento removível. Em vez disso, alguns dispositivos alocarão parte de sua memória interna não volátil para o Android, que pode executar a mesma função que a mídia removível. Isso é conhecido como armazenamento emulado e ainda é considerado armazenamento externo. Como alternativa, alguns dispositivos Android podem ter várias partições de armazenamento externas. Por exemplo, um tablet Android (além de seu armazenamento interno) pode ter armazenamento emulado e um ou mais slots para um cartão SD. Todas essas partições são tratadas pelo Android como armazenamento externo.
Em dispositivos com vários usuários, cada usuário terá um diretório dedicado na partição de armazenamento externo primária para seu armazenamento externo. Os aplicativos executados como um usuário não terão acesso aos arquivos de outro usuário no dispositivo. Os arquivos de todos os usuários ainda podem ser lidos e gravados por todos; no entanto, o Android colocará cada perfil de usuário em sandbox dos outros.
A leitura e a gravação em arquivos são quase idênticas no Xamarin.Android e em qualquer outro aplicativo .NET. O aplicativo Xamarin.Android determina o caminho para o arquivo que será manipulado e, em seguida, usa idiomas .NET padrão para acesso ao arquivo. Como os caminhos reais para o armazenamento interno e externo podem variar de dispositivo para dispositivo ou de versão para versão do Android, não é recomendável codificar o caminho para os arquivos. Em vez disso, o Xamarin.Android expõe as APIs nativas do Android que ajudarão a determinar o caminho para arquivos no armazenamento interno e externo.
Este guia discutirá os conceitos e APIs no Android específicos para armazenamento externo.
Arquivos públicos e privados no armazenamento externo
Existem dois tipos diferentes de arquivos que um aplicativo pode manter no armazenamento externo:
Arquivos privados – Arquivos privados são arquivos específicos do seu aplicativo (mas ainda podem ser lidos e graváveis por todos). O Android espera que os arquivos privados sejam armazenados em um diretório específico no armazenamento externo. Mesmo que os arquivos sejam chamados de "privados", eles ainda são visíveis e acessíveis por outros aplicativos no dispositivo, eles não recebem nenhuma proteção especial do Android.
Arquivos públicos – são arquivos que não são considerados específicos do aplicativo e devem ser compartilhados livremente.
As diferenças entre esses arquivos são principalmente conceituais. Os arquivos privados são privados no sentido de que são considerados parte do aplicativo, enquanto os arquivos públicos são quaisquer outros arquivos existentes no armazenamento externo. O Android fornece duas APIs diferentes para resolver os caminhos para arquivos privados e públicos, mas, caso contrário, as mesmas APIs do .NET são usadas para ler e gravar nesses arquivos. Essas são as mesmas APIs discutidas na seção sobre leitura e gravação.
Arquivos externos privados
Os arquivos externos privados são considerados específicos de um aplicativo (semelhante aos arquivos internos), mas estão sendo mantidos no armazenamento externo por vários motivos (como serem muito grandes para armazenamento interno). Semelhante aos arquivos internos, esses arquivos serão excluídos quando o aplicativo for desinstalado pelo usuário.
O local principal para arquivos externos privados é encontrado chamando o método Android.Content.Context.GetExternalFilesDir(string type)
. Esse método retornará um Java.IO.File
objeto que representa o diretório de armazenamento externo privado do aplicativo. Passar null
para esse método retornará o caminho para o diretório de armazenamento do usuário para o aplicativo. Por exemplo, para um aplicativo com o nome com.companyname.app
do pacote, o diretório "raiz" dos arquivos externos privados seria:
/storage/emulated/0/Android/data/com.companyname.app/files/
Este documento fará referência ao diretório de armazenamento para arquivos privados no armazenamento externo conforme PRIVATE_EXTERNAL_STORAGE.
O parâmetro for GetExternalFilesDir()
é uma cadeia de caracteres que especifica um diretório de aplicativo. Este é um diretório destinado a fornecer um local padrão para uma organização lógica de arquivos. Os valores de string estão disponíveis por meio de constantes na Android.OS.Environment
classe:
Android.OS.Ambiente | Diretório |
---|---|
Alarmes de diretório | PRIVATE_EXTERNAL_STORAGE/Alarmes |
DirectoryDcim | PRIVATE_EXTERNAL_STORAGE/DCIM |
DiretórioDownloads | PRIVATE_EXTERNAL_STORAGE/Baixar |
Documentos do diretório | PRIVATE_EXTERNAL_STORAGE/Documentos |
DiretórioFilmes | PRIVATE_EXTERNAL_STORAGE/Filmes |
DiretórioMúsica | PRIVATE_EXTERNAL_STORAGE/Música |
Notificações de diretório | PRIVATE_EXTERNAL_STORAGE/Notificações |
DiretórioPodcasts | PRIVATE_EXTERNAL_STORAGE/Podcasts |
Toques de diretório | PRIVATE_EXTERNAL_STORAGE/Toques |
Imagens do diretório | PRIVATE_EXTERNAL_STORAGE/Fotos |
Para dispositivos que têm várias partições de armazenamento externo, cada partição terá um diretório destinado a arquivos privados. O método Android.Content.Context.GetExternalFilesDirs(string type)
retornará uma matriz de Java.IO.Files
. Cada objeto representará um diretório privado específico do aplicativo em todos os dispositivos de armazenamento compartilhados/externos em que o aplicativo pode colocar os arquivos que possui.
Importante
O caminho exato para o diretório de armazenamento externo privado pode variar de dispositivo para dispositivo e entre versões do Android. Por isso, os aplicativos não devem codificar o caminho para esse diretório e, em vez disso, usar as APIs do Xamarin.Android, como Android.Content.Context.GetExternalFilesDir()
.
Arquivos externos públicos
Arquivos públicos são arquivos que existem no armazenamento externo que não são armazenados no diretório que o Android aloca para arquivos privados. Os arquivos públicos não serão excluídos quando o aplicativo for desinstalado. Os aplicativos Android devem receber permissão antes de poderem ler ou gravar qualquer arquivo público. É possível que arquivos públicos existam em qualquer lugar no armazenamento externo, mas, por convenção, o Android espera que os arquivos públicos existam no diretório identificado pela propriedade Android.OS.Environment.ExternalStorageDirectory
. Essa propriedade retornará um Java.IO.File
objeto que representa o diretório de armazenamento externo primário. Como exemplo, Android.OS.Environment.ExternalStorageDirectory
pode se referir ao seguinte diretório:
/storage/emulated/0/
Este documento fará referência ao diretório de armazenamento para arquivos públicos no armazenamento externo conforme PUBLIC_EXTERNAL_STORAGE.
O Android também suporta o conceito de diretórios de aplicativos no PUBLIC_EXTERNAL_STORAGE. Esses diretórios são exatamente iguais aos diretórios do aplicativo e PRIVATE_EXTERNAL_STORAGE
são descritos na tabela da seção anterior. O método Android.OS.Environment.GetExternalStoragePublicDirectory(string directoryType)
retornará um Java.IO.File
objeto que corresponde a um diretório de aplicativo público. O directoryType
parâmetro é um parâmetro obrigatório e não pode ser null
.
Por exemplo, chamar Environment.GetExternalStoragePublicDirectory(Environment.DirectoryDocuments).AbsolutePath
retornará uma string que será semelhante a:
/storage/emulated/0/Documents
Importante
O caminho exato para o diretório de armazenamento externo público pode variar de dispositivo para dispositivo e entre versões do Android. Por isso, os aplicativos não devem codificar o caminho para esse diretório e, em vez disso, usar as APIs do Xamarin.Android, como Android.OS.Environment.ExternalStorageDirectory
.
Trabalhando com armazenamento externo
Depois que um aplicativo Xamarin.Android obtém o caminho completo para um arquivo, ele deve utilizar qualquer uma das APIs padrão do .NET para criar, ler, gravar ou excluir arquivos. Isso maximiza a quantidade de código compatível com várias plataformas para um aplicativo. No entanto, antes de tentar acessar um arquivo, um aplicativo Xamarin.Android deve garantir que seja possível acessar esse arquivo.
- Verificar armazenamento externo – Dependendo da natureza do armazenamento externo, é possível que ele não seja montado e utilizável pelo aplicativo. Todos os aplicativos devem verificar o estado do armazenamento externo antes de tentar usá-lo.
- Executar uma verificação de permissão de tempo de execução – Um aplicativo Android deve solicitar permissão do usuário para acessar o armazenamento externo. Isso significa que uma solicitação de permissão de tempo de execução deve ser executada antes de qualquer acesso ao arquivo. O guia Permissões no Xamarin.Android contém mais detalhes sobre as permissões do Android.
Cada uma dessas duas tarefas será discutida abaixo.
Verificando se o armazenamento externo está disponível
A primeira etapa antes de gravar no armazenamento externo é verificar se ele pode ser lido ou gravado. A Android.OS.Environment.ExternalStorageState
propriedade contém uma cadeia de caracteres que identifica o estado do armazenamento externo. Essa propriedade retornará uma cadeia de caracteres que representa o estado. Esta tabela é uma lista dos ExternalStorageState
valores que podem ser retornados por Environment.ExternalStorageState
:
ExternalStorageState | Descrição |
---|---|
Remoção de mídia ruim | A mídia foi removida abruptamente sem ser devidamente desmontada. |
Verificação de mídia | A mídia está presente, mas passando por uma verificação de disco. |
Ejeção de mídia | A mídia está em processo de ser desmontada e ejetada. |
Montado em mídia | A mídia é montada e pode ser lida ou gravada. |
MediaMountedReadOnly | A mídia está montada, mas só pode ser lida. |
Nomes de mídia | A mídia está presente, mas não contém um sistema de arquivos adequado para Android. |
Mídia removida | Não há mídia presente. |
Mídia compartilhada | A mídia está presente, mas não está montada. Ele está sendo compartilhado via USB com outro dispositivo. |
MídiaDesconhecido | O estado da mídia não é reconhecido pelo Android. |
MídiaDesmontável | A mídia está presente, mas não pode ser montada pelo Android. |
MídiaNão montado | A mídia está presente, mas não está montada. |
A maioria dos aplicativos Android só precisa verificar se o armazenamento externo está montado. O snippet de código a seguir mostra como verificar se o armazenamento externo está montado para acesso somente leitura ou acesso de leitura/gravação:
bool isReadonly = Environment.MediaMountedReadOnly.Equals(Environment.ExternalStorageState);
bool isWriteable = Environment.MediaMounted.Equals(Environment.ExternalStorageState);
Permissões de armazenamento externo
O Android considera o acesso ao armazenamento externo uma permissão perigosa, que normalmente exige que o usuário conceda permissão para acessar o recurso. O usuário pode revogar essa permissão a qualquer momento. Isso significa que uma solicitação de permissão de tempo de execução deve ser executada antes de qualquer acesso ao arquivo. Os aplicativos recebem automaticamente permissões para ler e gravar seus próprios arquivos privados. É possível que os aplicativos leiam e gravem os arquivos privados que pertencem a outros aplicativos após receberem permissão do usuário.
Todos os aplicativos Android devem declarar uma das duas permissões para armazenamento externo no AndroidManifest.xml . Para identificar as permissões, um dos dois uses-permission
elementos a seguir deve ser adicionado a AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Observação
Se o usuário conceder WRITE_EXTERNAL_STORAGE
, então READ_EXTERNAL_STORAGE
também será implicitamente concedido. Não é necessário solicitar ambas as permissões em AndroidManifest.xml.
As permissões também podem ser adicionadas usando a guia Manifesto do Android das propriedades da solução:
De um modo geral, todas as permissões perigosas devem ser aprovadas pelo usuário. As permissões para armazenamento externo são uma anomalia, pois há exceções a essa regra, dependendo da versão do Android que o aplicativo está executando:
Para obter mais informações sobre como executar solicitações de permissão de runtime, consulte o guia Permissões no Xamarin.Android. O LocalFiles de amostra monodroid também demonstra uma maneira de executar verificações de permissão de tempo de execução.
Concedendo e revogando permissões com o ADB
Durante o desenvolvimento de um aplicativo Android, pode ser necessário conceder e revogar permissões para testar os vários fluxos de trabalho envolvidos nas verificações de permissão de tempo de execução. É possível fazer isso no prompt de comando usando ADB. Os snippets de linha de comando a seguir demonstram como conceder ou revogar permissões usando o ADB para um aplicativo Android cujo nome do pacote é 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
Excluindo arquivos
Qualquer uma das APIs C# padrão pode ser usada para excluir um arquivo do armazenamento externo, como System.IO.File.Delete
. Também é possível usar as APIs Java em detrimento da portabilidade do código. Por exemplo:
System.IO.File.Delete("/storage/emulated/0/Android/data/com.companyname.app/files/count.txt");