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 a partir de fotos Use uma abordagem se o app usar armazenamento com escopo. Use uma abordagem diferente se o app desativar o armazenamento com escopo.
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. Recupere 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.

Acessar informações de local a partir de fotos

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

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 ao usuário permissão 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 no seu app precisa da permissão.

Saiba mais sobre como executar operações em lote usando esses métodos disponíveis no Android 11.

Em execução no Android 10

Se o app for destinado ao Android 10 (API de nível 29), desative o armazenamento com escopo e continue usando a abordagem para o 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 para o usuário, use setType() ou EXTRA_MIME_TYPES.

Capturar uma única imagem

Quando você quiser capturar uma única imagem para usar no app (por exemplo, para usar como foto no perfil de um usuário), use a intent ACTION_IMAGE_CAPTURE para pedir 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 mais informações, consulte Acessar arquivos usando caminhos brutos.

Em execução no Android 10

Se o app for destinado ao Android 10 (API de nível 29), desative o armazenamento com escopo e continue usando a abordagem para o 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.
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.

Abrir um arquivo de documento

Use a intent ACTION_OPEN_DOCUMENT para 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);

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 seu app for destinado ao Android 11
  1. Use a sinalização preserveLegacyExternalStorage para preservar o modelo de armazenamento legado para que o app possa migrar os dados de um usuário quando ele fizer upgrade para a nova versão do app direcionado ao Android 11.

  2. Continue a desativar o armazenamento com escopo para que o app possa continuar acessando seus arquivos no local de armazenamento legado em dispositivos 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 migrar, use a seguinte abordagem:

  1. Verifique se os arquivos de trabalho do app estão localizados no diretório /sdcard/ ou em qualquer um dos subdiretórios.
  2. Mova todos os arquivos particulares do app do local atual em /sdcard/ para o diretório retornado pelo método getExternalFilesDir().
  3. Mova todos os arquivos compartilhados que não sejam de mídia do local atual em /sdcard/ 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/.

Compartilhar conteúdo com outros apps

Para compartilhar os arquivos do seu app com outro app, 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.

Desativar temporariamente o armazenamento com escopo

Antes que o app seja totalmente compatível com o armazenamento com escopo, é possível desativá-lo temporariamente usando um dos métodos a seguir:

  • Direcione-o ao Android 9 (API de nível 28) ou versão anterior.
  • Se você direcionar ao Android 10 (API de nível 29) ou versão mais recente, defina o valor de requestLegacyExternalStorage como true no arquivo de manifesto do app:

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

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