Armazenamento de Arquivos e Acesso com Xamarin.Android

Um requisito comum para aplicativos Android é manipular arquivos – salvar imagens, baixar documentos ou exportar dados para compartilhar com outros programas. O Android (que é baseado no Linux) dá suporte a isso fornecendo espaço para armazenamento de arquivos. O Android agrupa o sistema de arquivos em dois tipos diferentes de armazenamento:

  • Armazenamento Interno – essa é uma parte do sistema de arquivos que só pode ser acessada pelo aplicativo ou pelo sistema operacional.
  • Armazenamento Externo – essa é uma partição para o armazenamento de arquivos que é acessível por todos os aplicativos, pelo usuário e possivelmente por outros dispositivos. Em alguns dispositivos, o armazenamento externo pode ser removível (como um cartão SD).

Esses agrupamentos são apenas conceituais e não se referem necessariamente a uma única partição ou diretório no dispositivo. Um dispositivo Android sempre fornecerá partição para armazenamento interno e armazenamento externo. É possível que determinados dispositivos possam ter várias partições consideradas armazenamento externo. Independentemente da partição, as APIs para ler, gravar ou criar arquivos são as mesmas. Há dois conjuntos de APIs que um aplicativo Xamarin.Android pode usar para acesso a arquivos:

  1. As APIs do .NET (fornecidas pelo Mono e encapsuladas pelo Xamarin.Android) – elas incluem os auxiliares do sistema de arquivos fornecidos pelo Xamarin.Essentials. As APIs do .NET fornecem a melhor compatibilidade entre plataformas e, como tal, o foco deste guia estará nessas APIs.
  2. As APIs nativas de acesso a arquivos Java (fornecidas pelo Java e encapsuladas pelo Xamarin.Android) – o Java fornece suas próprias APIs para ler e gravar arquivos. Essas são uma alternativa completamente aceitável para as APIs do .NET, mas são específicas do Android e não são adequadas para aplicativos que se destinam a ser multiplataforma.

Ler e gravar em arquivos é quase idêntico no Xamarin.Android como em qualquer outro aplicativo .NET. O aplicativo Xamarin.Android determina o caminho para o arquivo que será manipulado e, em seguida, usa expressões padrão do .NET para acesso a arquivos. Como os caminhos reais para o armazenamento interno e externo podem variar de dispositivo para dispositivo ou da versão do Android para a versão do Android, não é recomendável codificar o caminho para os arquivos. Em vez disso, use as APIs do Xamarin.Android para determinar o caminho para os arquivos. Dessa forma, as APIs do .NET para ler e gravar arquivos expõem as APIs nativas do Android que ajudarão a determinar o caminho para arquivos no armazenamento interno e externo.

Antes de discutir as APIs envolvidas com o acesso a arquivos, é importante entender alguns dos detalhes em torno do armazenamento interno e externo. Isso será discutido na próxima seção.

Armazenamento interno versus externo

Conceitualmente, o armazenamento interno e o armazenamento externo são muito semelhantes– ambos são locais em que um aplicativo Xamarin.Android pode salvar arquivos. Essa similaridade pode ser confusa para desenvolvedores que não estão familiarizados com o Android, pois não está claro quando um aplicativo deve usar armazenamento interno versus armazenamento externo.

O armazenamento interno refere-se à memória não volátil que o Android aloca para o sistema operacional, APKs e para aplicativos individuais. Esse espaço não é acessível, exceto pelo sistema operacional ou aplicativos. O Android alocará um diretório na partição de armazenamento interno para cada aplicativo. Quando o aplicativo for desinstalado, todos os arquivos mantidos no armazenamento interno nesse diretório também serão excluídos. O armazenamento interno é mais adequado para arquivos que só podem ser acessados pelo aplicativo e que não serão compartilhados com outros aplicativos ou terão muito pouco valor depois que o aplicativo for desinstalado. No Android 6.0 ou superior, os arquivos no armazenamento interno podem ser automaticamente copiados pelo Google usando o recurso de Backup Automático no Android 6.0. O armazenamento interno tem as seguintes desvantagens:

  • Os arquivos não podem ser compartilhados.
  • Os arquivos serão excluídos quando o aplicativo for desinstalado.
  • O espaço disponível no armazenamento interno talvez seja limitado.

Armazenamento externo refere-se ao armazenamento de arquivos que não é armazenamento interno e não é exclusivamente acessível a um aplicativo. A principal finalidade 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. A vantagem do armazenamento externo é que ele normalmente tem muito mais espaço para arquivos do que o armazenamento interno. No entanto, nem sempre há garantia de que o armazenamento externo esteja presente em um dispositivo e pode exigir permissão especial do usuário para acessá-lo.

Observação

Para dispositivos que dão suporte a vários usuários, o Android fornecerá a cada usuário seu próprio diretório no armazenamento interno e externo. Esse diretório é inacessível para outros usuários no dispositivo. Essa separação é invisível para aplicativos, desde que eles não decodificem caminhos para arquivos no armazenamento interno ou externo.

Como regra geral, os aplicativos Xamarin.Android devem preferir salvar seus arquivos no armazenamento interno quando for razoável e depender do armazenamento externo quando os arquivos precisam ser compartilhados com outros aplicativos, são muito grandes ou devem ser retidos mesmo se o aplicativo for desinstalado. Por exemplo, um arquivo de configuração é mais adequado para um armazenamento interno, pois ele não tem importância, exceto para o aplicativo que o cria. Por outro lado, as fotos são boas candidatas ao armazenamento externo. Eles podem ser muito grandes e, em muitos casos, o usuário pode querer compartilhá-los ou acessá-los mesmo que o aplicativo esteja desinstalado.

Este guia se concentrará no armazenamento interno. Consulte o guia Armazenamento externo para obter detalhes sobre como usar o armazenamento externo em um aplicativo Xamarin.Android.

Trabalhando com armazenamento interno

O diretório de armazenamento interno de um aplicativo é determinado pelo sistema operacional e é exposto a aplicativos Android pela Android.Content.Context.FilesDir propriedade . Isso retornará um Java.IO.File objeto que representa o diretório que o Android dedicou exclusivamente para o aplicativo. Por exemplo, um aplicativo com o nome do pacote com.companyname o diretório de armazenamento interno pode ser:

/data/user/0/com.companyname/files

Este documento se referirá ao diretório de armazenamento interno como INTERNAL_STORAGE.

Importante

O caminho exato para o diretório de armazenamento interno pode variar de dispositivo para dispositivo e entre versões do Android. Por isso, os aplicativos não devem codificar o caminho para o diretório de armazenamento de arquivos internos e, em vez disso, usar as APIs Xamarin.Android, como System.Environment.GetFolderPath().

Para maximizar o compartilhamento de código, os aplicativos Xamarin.Android (ou aplicativos Xamarin.Forms direcionados ao Xamarin.Android) devem usar o System.Environment.GetFolderPath() método . No Xamarin.Android, esse método retornará uma cadeia de caracteres para um diretório que é o mesmo local Android.Content.Context.FilesDirque . Esse método usa uma enumeração , System.Environment.SpecialFolder, que é usada para identificar um conjunto de constantes enumeradas que representam os caminhos de pastas especiais usadas pelo sistema operacional. Nem todos os System.Environment.SpecialFolder valores serão mapeados para um diretório válido no Xamarin.Android. A tabela a seguir descreve qual caminho pode ser esperado para um determinado valor de System.Environment.SpecialFolder:

System.Environment.SpecialFolder Caminho
ApplicationData INTERNAL_STORAGE/.config
Desktop INTERNAL_STORAGE/Desktop
LocalApplicationData INTERNAL_STORAGE/.local/share
MyDocuments INTERNAL_STORAGE
MyMusic INTERNAL_STORAGE/Música
MyPictures INTERNAL_STORAGE/Imagens
MyVideos INTERNAL_STORAGE/Vídeos
Personal INTERNAL_STORAGE
Fonts INTERNAL_STORAGE/.fonts
Templates INTERNAL_STORAGE/Modelos
CommonApplicationData /usr/share
CommonApplicationData /usr/share

Leitura ou gravação em arquivos no armazenamento interno

Qualquer uma das APIs C# para gravar em um arquivo é suficiente; tudo o que é necessário é obter o caminho para o arquivo que está no diretório alocado para o aplicativo. É altamente recomendável que as versões assíncronas das APIs do .NET sejam usadas para minimizar quaisquer problemas que possam ser associados ao acesso a arquivos bloqueando o thread main.

Este snippet de código é um exemplo de gravação de um inteiro em um arquivo de texto UTF-8 no diretório de armazenamento interno de um aplicativo:

public async Task SaveCountAsync(int count)
{
    var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "count.txt");
    using (var writer = File.CreateText(backingFile))
    {
        await writer.WriteLineAsync(count.ToString());
    }
}

O próximo snippet de código fornece uma maneira de ler um valor inteiro que foi armazenado em um arquivo de texto:

public async Task<int> ReadCountAsync()
{
    var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "count.txt");

    if (backingFile == null || !File.Exists(backingFile))
    {
        return 0;
    }

    var count = 0;
    using (var reader = new StreamReader(backingFile, true))
    {
        string line;
        while ((line = await reader.ReadLineAsync()) != null)
        {
            if (int.TryParse(line, out var newcount))
            {
                count = newcount;
            }
        }
    }

    return count;
}

Usando o Xamarin.Essentials – Auxiliares do Sistema de Arquivos

O Xamarin.Essentials é um conjunto de APIs para escrever código compatível entre plataformas. Os Auxiliares do Sistema de Arquivos são uma classe que contém uma série de auxiliares para simplificar a localização do cache e dos diretórios de dados do aplicativo. Este snippet de código fornece um exemplo de como localizar o diretório de armazenamento interno e o diretório de cache de um aplicativo:

// Get the path to a file on internal storage
var backingFile = Path.Combine(Xamarin.Essentials.FileSystem.AppDataDirectory, "count.txt");

// Get the path to a file in the cache directory
var cacheFile = Path.Combine(Xamarin.Essentials.FileSystem.CacheDirectory, "count.txt");

Ocultando arquivos do MediaStore

O MediaStore é um componente android que coleta metadados sobre arquivos de mídia (vídeos, músicas, imagens) em um dispositivo Android. Sua finalidade é simplificar o compartilhamento desses arquivos em todos os aplicativos Android no dispositivo.

Os arquivos privados não aparecerão como mídia compartilhável. Por exemplo, se um aplicativo salvar uma imagem em seu armazenamento externo privado, esse arquivo não será coletado pelo verificador de mídia (MediaStore).

Os arquivos públicos serão coletados por MediaStore. Diretórios que têm um nome de arquivo de byte zero . NOMEDIA não será verificado por MediaStore.