Casos de uso e práticas recomendadas de armazenamento

Para dar aos usuários mais controle sobre os arquivos e limitar a desorganização dos arquivos, o Android 10 introduziu um novo modelo de armazenamento para apps chamado armazenamento com escopo. O armazenamento com escopo muda a forma como os apps armazenam e acessam arquivos no armazenamento externo de um dispositivo. Para migrar seu app para oferecer suporte ao armazenamento com escopo, siga as práticas recomendadas para casos de uso de armazenamento comuns descritas neste guia. Os casos de uso são organizados em duas categorias: gerenciamento de arquivos de mídia e gerenciamento de arquivos que não são de mídia.

Para saber mais sobre como armazenar e acessar arquivos no Android, consulte os guias de treinamento de armazenamento.

Gerenciar arquivos de mídia

Esta seção descreve alguns dos casos de uso comuns para lidar com arquivos de mídia (arquivos de vídeo, imagem e áudio) e explica a abordagem de alto nível que seu app pode usar. A tabela a seguir resume cada um desses casos de uso e links para cada uma das seções que contêm mais detalhes.

Caso de uso Resumo
Mostrar todos os arquivos de imagem ou vídeo Use a mesma abordagem para todas as versões do Android.
Mostrar imagens ou vídeos de uma pasta específica Use a mesma abordagem para todas as versões do Android.
Acessar informações de localização de fotos Use uma abordagem se o app usar armazenamento com escopo. Use uma abordagem diferente se o app desativar o armazenamento com escopo.
Definir o local de armazenamento para novos downloads Use uma abordagem se o app usar armazenamento com escopo. Use uma abordagem diferente se o app desativar o armazenamento com escopo.
Exportar arquivos de mídia do usuário para um dispositivo Use a mesma abordagem para todas as versões do Android.
Modificar ou excluir vários arquivos de mídia em uma única operação Use uma abordagem para o Android 11. Para o Android 10, desative o armazenamento com escopo e use a abordagem para o Android 9 e versões anteriores.
Importar uma única imagem que já existe Use a mesma abordagem para todas as versões do Android.
Capturar uma única imagem Use a mesma abordagem para todas as versões do Android.
Compartilhar arquivos de mídia com outros apps Use a mesma abordagem para todas as versões do Android.
Compartilhar arquivos de mídia com um app específico Use a mesma abordagem para todas as versões do Android.
Acessar arquivos de código ou bibliotecas que usam caminhos de arquivo diretos Use uma abordagem para o Android 11. Para o Android 10, desative o armazenamento com escopo e use a abordagem para o Android 9 e versões anteriores.

Mostrar arquivos de imagem ou vídeo de várias pastas

Consulte uma coleção de mídia usando a API query(). Para filtrar ou classificar os arquivos de mídia, ajuste os parâmetros projection, selection, selectionArgs e sortOrder.

Mostrar imagens ou vídeos de uma pasta específica

Use esta abordagem:

  1. Seguindo as práticas recomendadas descritas em Solicitar permissões do app, solicite a permissão READ_EXTERNAL_STORAGE.
  2. Extraia arquivos de mídia com base no valor de MediaColumns.DATA, que contém o caminho absoluto do sistema de arquivos para o item de mídia no disco.

Observação: ao acessar um arquivo de mídia existente, você pode usar o valor da coluna DATA na sua lógica. Isso ocorre porque esse valor tem um caminho de arquivo válido. No entanto, não presuma que o arquivo vai estar sempre disponível. Esteja preparado para processar todos os erros de E/S causados por arquivo que possam ocorrer.

Por outro lado, para criar ou atualizar um arquivo de mídia, não use a coluna DATA. Em vez disso, use as colunas DISPLAY_NAME e RELATIVE_PATH.

Acessar informações de local de fotos

Se o app usa o armazenamento com escopo, siga as etapas na seção Informações de localização em fotografias do guia de armazenamento de mídia.

Definir o local de armazenamento para novos downloads

Se o app usa armazenamento com escopo, preste atenção ao local escolhido para armazenar os arquivos de mídia transferidos por download.

Caso outros apps precisam acessar os arquivos, use coleções de mídia bem definidas para coleções de downloads ou de documentos.

No Android 11 e versões mais recentes, os arquivos no diretório externo específico do app não podem ser acessados por outros apps, mesmo que você use DownloadManager para buscar esses arquivos.

Exportar arquivos de mídia do usuário para um dispositivo

Defina um local padrão apropriado para armazenar arquivos de mídia do usuário:

Modificar ou excluir vários arquivos de mídia em uma única operação

Incorpore a lógica com base nas versões do Android em que seu app é executado.

Em execução no Android 11

Use esta abordagem:

  1. Crie uma intent pendente para a solicitação de gravação ou exclusão do app usando MediaStore.createWriteRequest() ou MediaStore.createTrashRequest() e solicite permissão do usuário para editar um conjunto de arquivos invocando essa intent.
  2. Avalie a resposta do usuário:

    • Se a permissão foi concedida, prossiga com a operação de modificação ou exclusão.
    • Se a permissão não foi concedida, explique ao usuário por que o recurso do app precisa da permissão.

Saiba mais sobre como gerenciar grupos de arquivos de mídia usando esses métodos, disponíveis no Android 11 e em versões mais recentes.

Em execução no Android 10

Se o app for destinado ao Android 10 (nível 29 da API), desative o armazenamento com escopo e continue usando a abordagem do Android 9 e versões anteriores para realizar essa operação.

Em execução no Android 9 ou versões anteriores

Use esta abordagem:

  1. Seguindo as práticas recomendadas descritas em Solicitar permissões do app, solicite a permissão WRITE_EXTERNAL_STORAGE.
  2. Use a API MediaStore para modificar ou excluir os arquivos de mídia.

Importar uma única imagem que já existe

Quando você quer importar uma única imagem que já existe (por exemplo, para usar como foto no perfil de um usuário), seu app poderá usar a própria IU para a operação ou o seletor do sistema.

Apresentar sua própria interface do usuário

Use esta abordagem:

  1. Seguindo as práticas recomendadas descritas em Solicitar permissões do app, solicite a permissão READ_EXTERNAL_STORAGE.
  2. Use a API query() para consultar uma coleção de mídia.
  3. Exiba os resultados na IU personalizada do seu app.

Usar o seletor de sistema

Use a intent ACTION_GET_CONTENT, que solicita que o usuário escolha uma imagem para importar.

Se você quiser filtrar os tipos de imagens que o seletor de sistema apresenta ao usuário, use setType() ou EXTRA_MIME_TYPES.

Capturar uma única imagem

Quando você quiser capturar uma única imagem que vai ser usada no app (por exemplo, para ser a foto do perfil de um usuário), use a intent ACTION_IMAGE_CAPTURE e peça ao usuário para tirar uma foto usando a câmera do dispositivo. O sistema armazena a foto capturada na tabela MediaStore.Images.

Compartilhar arquivos de mídia com outros apps

Use o método insert() para adicionar registros diretamente ao MediaStore. Para mais informações, consulte a seção Adicionar um item do guia de armazenamento de mídia.

Compartilhar arquivos de mídia com um app específico

Use o componente FileProvider do Android, conforme descrito no guia Como configurar o compartilhamento de arquivos.

Acessar arquivos de código ou bibliotecas que usam caminhos de arquivo diretos

Incorpore a lógica com base nas versões do Android em que seu app é executado.

Em execução no Android 11

Use esta abordagem:

  1. Seguindo as práticas recomendadas descritas em Solicitar permissões do app, solicite a permissão READ_EXTERNAL_STORAGE.
  2. Acesse os arquivos usando caminhos de arquivo diretos.

Para saber mais, consulte a seção sobre como abrir arquivos de mídia usando caminhos diretos para os arquivos.

Em execução no Android 10

Se o app for destinado ao Android 10 (nível 29 da API), desative o armazenamento com escopo e continue usando a abordagem do Android 9 e versões anteriores para realizar essa operação.

Em execução no Android 9 ou versões anteriores

Use esta abordagem:

  1. Seguindo as práticas recomendadas descritas em Solicitar permissões do app, solicite a permissão WRITE_EXTERNAL_STORAGE.
  2. Acesse os arquivos usando caminhos de arquivo diretos.

Gerenciar arquivos que não são de mídia

Esta seção descreve alguns dos casos de uso comuns para gerenciar arquivos que não são de mídia e explica a abordagem de alto nível que seu app pode usar. A tabela a seguir resume cada um desses casos de uso e links para cada uma das seções que contêm mais detalhes.

Caso de uso Resumo
Abrir um arquivo de documento Use a mesma abordagem para todas as versões do Android.
Fazer modificações em arquivos em volumes de armazenamentos secundários Use uma abordagem para o Android 11. Use uma abordagem diferente para versões anteriores do Android.
Migrar arquivos existentes de um local de armazenamento legado Migre seus arquivos para o armazenamento com escopo quando possível. Desative o armazenamento com escopo para o Android 10 quando necessário.
Compartilhar conteúdo com outros apps Use a mesma abordagem para todas as versões do Android.
Armazenar em cache arquivos que não sejam de mídia Use a mesma abordagem para todas as versões do Android.
Exportar arquivos que não são de mídia para um dispositivo Use uma abordagem se o app usar armazenamento com escopo. Use uma abordagem diferente se o app desativar o armazenamento com escopo.

Abrir um arquivo de documento

Use a intent ACTION_OPEN_DOCUMENT a fim de solicitar que o usuário escolha um arquivo para abrir usando o seletor do sistema. Se você quiser filtrar os tipos de arquivos que o seletor de sistema apresentará ao usuário, use setType() ou EXTRA_MIME_TYPES.

Por exemplo, você pode encontrar todos os arquivos PDF, ODT e TXT usando o seguinte código:

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);

Fazer modificações em arquivos em volumes de armazenamento secundários

Os volumes de armazenamento secundário incluem cartões SD. É possível acessar informações sobre um determinado volume de armazenamento usando a classe StorageVolume.

Incorpore a lógica de acordo com as versões do Android em que o app é executado.

No Android 11

Use esta abordagem:

  1. Use o modelo de armazenamento com escopo.
  2. Destine o app ao Android 10 (nível 29 da API) ou versões anteriores.
  3. Declare a permissão WRITE_EXTERNAL_STORAGE.
  4. Execute um destes tipos de acesso:
    • Acesso aos arquivos usando a API MediaStore.
    • Acesso aos arquivos usando o caminho direito com APIs como File ou fopen().

Em versões anteriores

Use o framework de acesso ao armazenamento, que permite que os usuários selecionem o local em um volume de armazenamento secundário em que o app pode modificar o arquivo.

Migrar arquivos existentes de um local de armazenamento legado

Um diretório é considerado um local de armazenamento legado se não for um diretório específico do app ou um diretório compartilhado público. Se o app criar ou consumir arquivos em um local de armazenamento legado, recomendamos que você migre os arquivos do app para locais acessíveis com armazenamento com escopo e faça as mudanças necessárias no app para trabalhar com arquivos no escopo.

Manter o acesso ao local de armazenamento legado para a migração de dados

Seu app precisa manter o acesso ao local de armazenamento legado para migrar os arquivos do app para locais acessíveis com armazenamento com escopo. A abordagem a ser usada depende do nível de API de destino do seu app.

Se o app for destinado ao Android 11
  1. Use a sinalização preserveLegacyExternalStorage em true para preservar o modelo de armazenamento legado a fim de permitir que o app possa migrar os dados de um usuário quando ele fizer upgrade para a nova versão do app destinada ao Android 11.

  2. Continue a desativar o armazenamento com escopo para que o app possa continuar acessando os arquivos no local de armazenamento legado em dispositivos com o Android 10.

Se o app for direcionado ao Android 10

Desative o armazenamento com escopo para facilitar a manutenção do comportamento do app nas versões do Android.

Migrar dados do app

Quando o app estiver pronto para migração, use esta abordagem:

  1. Destine o app ao Android 10 ou versões anteriores
  2. Desative o armazenamento com escopo para que o app tenha acesso aos arquivos que você precisa migrar.
  3. Implante código que usa a API File para mover arquivos do local atual, em /sdcard/, para um local acessível pelo armazenamento com escopo:

    1. Mova todos os arquivos particulares do app para o diretório retornado pelo método getExternalFilesDir().
    2. Mova todos os arquivos compartilhados que não sejam de mídia para um subdiretório dedicado ao app do diretório Downloads/.
  4. Remova os diretórios de armazenamento legados do app do diretório /sdcard/.

Após instalar a nova versão do app, o usuário finaliza o processo de migração de dados no dispositivo. É possível monitorar o processo de migração de toda a base de usuários criando um evento de análise.

Depois que os usuários migrarem os dados, publique outra atualização no app, destinando-o ao Android 11.

Compartilhar conteúdo com outros apps

Para compartilhar os arquivos do seu app com outro, use um FileProvider. Para apps que precisam compartilhar arquivos entre si, recomendamos usar um provedor de conteúdo para cada app e sincronizar os dados à medida que eles forem adicionados à coleção.

Armazenar em cache arquivos que não sejam de mídia

A abordagem a ser usada depende do tipo de arquivo que você precisa armazenar em cache.

Exportar arquivos que não são de mídia para um dispositivo

Defina um local padrão adequado para armazenar arquivos que não sejam de mídia. Permita que os usuários exportem arquivos de diretórios específicos do app para um local mais acessível. Use as coleções de downloads ou de documentos da MediaStore a fim de exportar os arquivos que não são de mídia para o dispositivo.

Desativar temporariamente o armazenamento com escopo

Antes que o app seja totalmente compatível com o armazenamento com escopo, é possível desativar esse recurso temporariamente, tanto nos testes quanto no app de produção.

Desativar em testes

No Android 10 (nível 29 da API) e versões mais recentes, por padrão, os testes do app são executados em um sandbox de armazenamento. Esse sandbox impede que o app acesse arquivos fora do diretório específico do app e de diretórios compartilhados publicamente.

Se um teste gerar arquivos para o host, como capturas de tela, dados de depuração, dados de cobertura ou métricas de desempenho, é possível gravar esses arquivos em diretórios globais. Para fazer isso, adicione a sinalização abaixo ao arcabouço correspondente que invoca am instrument:

-e no-isolated-storage 1

Essa sinalização afeta todo o comportamento do caso de teste instrumentado e todo o código de teste invocado. Portanto, ao usar essa sinalização, não é possível validar a compatibilidade do app com o armazenamento com escopo. Para a saída de teste, é melhor gravar no armazenamento com escopo do app que pode ser lido pelo shell. Em seguida, é possível retirar esse diretório com escopo do app. Para determinar qual diretório vai ser retirado, chame getExternalMediaDirs().

Desativar no app de produção

Se o app for direcionado ao Android 10 (nível 29 da API) ou versões anteriores, você pode desativar temporariamente o armazenamento com escopo no app de produção. No entanto, se ele for direcionado ao Android 10, é necessário definir o valor de requestLegacyExternalStorage como true no arquivo de manifesto do app:

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

Para testar como um app direcionado ao Android 10 ou versões anteriores se comporta ao usar o armazenamento com escopo, ative esse comportamento definindo o valor de requestLegacyExternalStorage como false. Se você estiver testando em um dispositivo com o Android 11, também é possível usar sinalizações de compatibilidade de apps para testar o comportamento do app com ou sem o armazenamento com escopo.

Outros recursos

Para ver mais informações sobre o armazenamento do Android, consulte os materiais abaixo:

Postagens do blog