Logo do Venturus
Android 10: Acesso a arquivos usando armazenamento por escopo
  • 28 de janeiro de 2020
  • Blog

Android 10: Acesso a arquivos usando armazenamento por escopo

No primeiro artigo, vimos as mudanças no acesso a arquivos ao longo da evolução do Android e as alterações implementadas no Android 10 pelo armazenamento por escopo, que foi criado para a melhorar a privacidade do usuário e o controle das informações armazenadas pelos aplicativos, uma vez que ele limita o acesso a informações sensíveis do usuário por aplicativos que não deveriam ter acesso a essas informações e garante a exclusão automática de todos os dados criados pelo aplicativo quando o mesmo é desinstalado.

A seguir, mostramos como um desenvolvedor deve adaptar seus aplicativos para se adequar a esse novo modo de acesso a arquivos.

 

Obrigatoriedade das mudanças

Se um aplicativo Android tiver como target o Android 10, as mudanças do armazenamento por escopo já estão ativas. Porém, enquanto não é obrigatório suportar o armazenamento por escopo no Android 10, ele será um requisito para o Android 11.

Caso o desenvolvedor do aplicativo deseje esperar pelo Android 11 para implementar as mudanças de acesso a arquivos e manter o comportamento atual, basta ativar o modo legado, que pode ser ativado colocando android:requestLegacyExternalStorage=”true” no arquivo Manifest do aplicativo.

 

Acesso a arquivos que não precisam ser compartilhados com outros aplicativos

Mesmo sem habilitar o modo legado, se um aplicativo apenas criar arquivos para armazenamento de dados que não precisam ser compartilhados com outros aplicativos, como mídias ou downloads, nenhuma alteração será necessária em sua implementação. Os arquivos criados pelo aplicativo serão armazenados num sistema de arquivos virtual que será transparente para o aplicativo.

Porém, aplicativos que costumavam acessar dados que não são seus e não são arquivos de mídia, não conseguirão mais fazer esse acesso. O acesso a arquivos genéricos somente será possível através do SAF (Storage Access Framework), biblioteca de acesso ao armazenamento de Androids. Para mais detalhes sobre o uso do SAF consulte.

 

Entendendo o local de armazenamento

Na implementação do armazenamento por escopo temos o conceito de volume, que indica em qual dispositivo (memória principal ou cartão de memória) os arquivos devem ser lidos ou gravados. Para a memória principal, uma constante foi criada para indicar este local de armazenamento, que é MediaStore.VOLUME_EXTERNAL_PRIMARY.

Porém, para o cartão de memória, precisamos chamar MediaStore.getExternalVolumeNames para obter a lista de todos os volumes disponíveis:

Lista de todos os volumes disponíveis MediaStore.getExternalVolumeNames

Lista de todos os volumes disponíveis MediaStore.getExternalVolumeNames

 

Como criar novos arquivos de mídia ou Downloads

Quando usamos armazenamento por escopo, não é necessário solicitar nenhuma permissão ao usuário para efetuar a gravação de arquivos, tanto na memória principal quanto no cartão de memória. O acesso é feito utilizando-se o content provider do MediaStore, de forma que é necessário solicitar a criação do novo arquivo para, em seguida, ter acesso de escrita ao mesmo.  Para mais detalhes do funcionamento do MediaStore consulte.

Criação de um novo arquivo:

Criação de um novo arquivo

Criação de um novo arquivo

 

Como vimos acima, passamos o nome do novo arquivo, o seu mime type e ligamos o flag IS_PENDING. Este flag é utilizado para avisar ao content provider que o arquivo ainda não está completo. Neste caso, quando qualquer aplicativo listar os arquivos disponíveis pelo MediaStore, os arquivos com este flag ativo não serão listados.

Como resultado, a variável item contém a URI do novo arquivo, que será utilizada para abri-lo para escrita e efetuar a gravação:

Variável item contém a URI do novo arquivo

Variável item contém a URI do novo arquivo

 

Ao finalizar as alterações ao arquivo, mudamos o flag IS_PENDING indicando que o arquivo está completo e atualizamos o MediaStore:

Arquivo está completo e atualizamos o MediaStor

Arquivo está completo e atualizamos o MediaStor

 

Listando os arquivos

Não foram introduzidas muitas mudanças para listar arquivos de mídia utilizando armazenamento por escopoDeve-se efetuar uma query no MediaStore solicitando os arquivos desejados da mesma maneira que era possível efetuar em versões anteriores de Android:

query no MediaStore

Query no MediaStore

 

Uma mudança importante implementada a partir do Android 10 foi a introdução do conceito de proprietário de arquivo. O proprietário é o nome do pacote do aplicativo que criou este arquivo. Como os arquivos agora possuem um aplicativo proprietário, quando uma query é feita no MediaStore, somente os arquivos criados pelo próprio aplicativo são listados. Porém, em dispositivos que tiveram upgrade para o Android 10, os arquivos criados anteriormente não possuem um proprietário, não sendo mais listados.

Para listar seus arquivos de mídia criados antes de um upgrade do sistema ou até arquivos de mídia criados por outros aplicativos, deve-se solicitar a permissão READ_EXTERNAL_STORAGE. Quando essa permissão é concedida, todos os arquivos de mídia (áudio, vídeo, imagens) são listados e liberados para leitura.

Um detalhe importante é que, apesar de ser possível gravar arquivos na pasta Downloads utilizando o armazenamento por escopo, não é possível fazer leitura ou listar os arquivos utilizando o content provider. Para ter acesso a esses arquivos, deve-se utilizar o SAF.

 

Efetuando leitura em arquivos

Uma vez que temos a URI do arquivo que desejamos abrir — que pode ser obtida através de uma query no MediaStore como mostrado anteriormente—, podemos abrir o arquivo para leitura. Lembre-se que arquivos em que seu aplicativo não é a proprietária ou arquivos gerados em versões anteriores de Android só poderão ser lidos com a permissão de READ_EXTERNAL_STORAGE.

Permissão de READ_EXTERNAL_STORAGE

Permissão de READ_EXTERNAL_STORAGE

 

Renomeando e apagando arquivos

Como o acesso aos arquivos de mídia é feito pela MediaStore, para renomear ou apagar arquivos, basta fazer a alteração no content provider e as mudanças são refletidas no sistema de arquivos.

Para excluir um arquivo:

Excluir um arquivo

Excluir um arquivo

 

Para renomear um arquivo:

Renomear um arquivo

Renomear um arquivo

 

Porém, para ambos os casos, estas operações são permitidas somente nos arquivos que foram criados pelo próprio aplicativo. Acesso a um arquivo de mídia de outros aplicativos ou arquivos próprios criados em versão antiga de Android geram a exceção RecoverableSecurityException.

Quando isto acontece ainda é possível executar a operação, mas precisamos pedir permissão para o usuário para que a operação seja concluída. Para isto, precisamos tratar essa exceção e solicitar essa permissão:

Permissão para o usuário

Permissão para o usuário

 

Depois que o usuário confirmar ou negar o acesso a este arquivo, a resposta é obtida no método onActivityResult:

método onActivityResult

Método onActivityResult

 

Conclusão

As mudanças introduzidas no Android 10 para o armazenamento por escopo trouxeram  O armazenamento por escopo também ofereceu a desenvolvedores uma solução única para acesso ao armazenamento principal e ao cartão SD.

Porém, mais uma vez, houve uma mudança grande nas APIs, obrigando os desenvolvedores a adaptar seus aplicativos para o Android 10 e manter implementações diferentes para cada versão do sistema operacional para manter a compatibilidade com versões anteriores.