Em muitos casos, o app cria arquivos que outros apps não precisam ou não podem acessar. O sistema fornece os seguintes locais para armazenar esses arquivos específicos do app:
Diretórios de armazenamento interno: esses diretórios incluem um local dedicado para armazenar arquivos persistentes e outro para armazenar dados em cache. O sistema impede que outros apps acessem esses locais. No Android 10 (API de nível 29) e versões mais recentes, esses locais são criptografados. Essas características fazem desses locais bons lugares para armazenar dados sensíveis que somente o próprio app pode acessar.
Diretórios de armazenamento externo: esses diretórios incluem um local dedicado para armazenar arquivos persistentes e outro para armazenar dados em cache. Embora seja possível que outro app acesse esses diretórios se ele tiver as permissões adequadas, os arquivos armazenados neles são apenas para uso do seu app. Para criar arquivos que outros apps possam acessar, seu app precisa os armazenar na parte de armazenamento compartilhado do armazenamento externo.
Quando o usuário desinstala o app, os arquivos salvos no armazenamento específico do app são removidos. Devido a esse comportamento, não use esse armazenamento para salvar dados que o usuário espera que persistam de forma independente do seu app. Por exemplo, se o app permite a captura de fotos, o usuário espera poder acessar essas fotos mesmo depois de desinstalar o app. Por isso, use o armazenamento compartilhado para salvar esses tipos de arquivo nas coleções de mídia adequadas.
As seções abaixo descrevem como armazenar e acessar arquivos em diretórios específicos do app.
Acessar do armazenamento interno
Para cada app, o sistema fornece diretórios no armazenamento interno, em que os apps podem organizar os próprios arquivos. Um diretório é criado para os arquivos persistentes do app e o outro contém os arquivos armazenados em cache. O app não precisa de permissões do sistema para ler e gravar arquivos nesses diretórios.
Os outros apps não podem acessar arquivos salvos no armazenamento interno. Isso faz com que o armazenamento interno seja um bom lugar para salvar os dados do app que outros apps não podem acessar.
No entanto, esses diretórios costumam ser pequenos. Antes de gravar arquivos específicos do app no armazenamento interno, o app precisa consultar o espaço livre no dispositivo.
Acessar arquivos persistentes
Os arquivos persistentes comuns do app ficam em um diretório que pode
ser acessado usando a propriedade filesDir
de um objeto de contexto. O framework fornece vários métodos para ajudar você a acessar e armazenar arquivos nesse diretório.
Acessar e armazenar arquivos
É possível usar a API File
para acessar e armazenar arquivos:
A fim de manter o bom desempenho do app, não abra e feche o mesmo arquivo várias vezes.
O snippet de código abaixo demonstra como usar a API File
:
Kotlin
val file = File(context.filesDir, filename)
Java
File file = new File(context.getFilesDir(), filename);
Armazenar um arquivo usando um stream
Como alternativa ao uso da API File
, você pode chamar
openFileOutput()
para acessar um FileOutputStream
que grava
em um arquivo no diretório filesDir
.
O snippet de código a seguir mostra como gravar texto em um arquivo:
Kotlin
val filename = "myfile" val fileContents = "Hello world!" context.openFileOutput(filename, Context.MODE_PRIVATE).use { it.write(fileContents.toByteArray()) }
Java
String filename = "myfile"; String fileContents = "Hello world!"; try (FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE)) { fos.write(fileContents.toByteArray()); }
Para permitir que outros apps acessem arquivos armazenados no
diretório no armazenamento interno, use um
FileProvider
com o atributo
FLAG_GRANT_READ_URI_PERMISSION
.
Acessar um arquivo usando um stream
Para ler um arquivo como um stream, use
openFileInput()
:
Kotlin
context.openFileInput(filename).bufferedReader().useLines { lines -> lines.fold("") { some, text -> "$some\n$text" } }
Java
FileInputStream fis = context.openFileInput(filename); InputStreamReader inputStreamReader = new InputStreamReader(fis, StandardCharsets.UTF_8); StringBuilder stringBuilder = new StringBuilder(); try (BufferedReader reader = new BufferedReader(inputStreamReader)) { String line = reader.readLine(); while (line != null) { stringBuilder.append(line).append('\n'); line = reader.readLine(); } } catch (IOException e) { // Error occurred when opening raw file for reading. } finally { String contents = stringBuilder.toString(); }
Ver lista de arquivos
É possível acessar uma matriz contendo os nomes de todos os arquivos no diretório filesDir
ao chamar
fileList()
, conforme mostrado no
snippet de código abaixo:
Kotlin
var files: Array<String> = context.fileList()
Java
Array<String> files = context.fileList();
Criar diretórios aninhados
Também é possível criar diretórios aninhados ou abrir um diretório interno chamando
getDir()
com um código baseado em Kotlin
ou transmitindo o diretório raiz e um novo nome de diretório para um construtor File
com um código baseado em Java:
Kotlin
context.getDir(dirName, Context.MODE_PRIVATE)
Java
File directory = context.getFilesDir(); File file = new File(directory, filename);
Criar arquivos em cache
Se você precisar armazenar dados confidenciais apenas temporariamente, use o diretório de cache designado do app no armazenamento interno para salvar os dados. Como acontece com todo o armazenamento específico do app, os arquivos armazenados nesse diretório são removidos quando o usuário desinstala o app, embora eles possam ser removidos antes.
Para criar um arquivo armazenado em cache, chame
File.createTempFile()
:
Kotlin
File.createTempFile(filename, null, context.cacheDir)
Java
File.createTempFile(filename, null, context.getCacheDir());
O app acessa arquivos nesse diretório usando a propriedade
cacheDir
de um
objeto de contexto e a API File
:
Kotlin
val cacheFile = File(context.cacheDir, filename)
Java
File cacheFile = new File(context.getCacheDir(), filename);
Remover arquivos em cache
Embora o Android às vezes exclua arquivos armazenados em cache por conta própria, não dependa do sistema para limpar esses arquivos. Sempre mantenha os arquivos do app salvos em cache no armazenamento interno.
Para remover um arquivo do diretório de cache no armazenamento interno, use um destes métodos:
O método
delete()
em um objetoFile
que representa o arquivo:Kotlin
cacheFile.delete()
Java
cacheFile.delete();
O método
deleteFile()
do contexto do app, transmitindo o nome do arquivo:Kotlin
context.deleteFile(cacheFileName)
Java
context.deleteFile(cacheFileName);
Acessar do armazenamento externo
Se o armazenamento interno não fornecer espaço suficiente para armazenar arquivos específicos do app, considere o uso do armazenamento externo. O sistema fornece diretórios dentro do armazenamento externo, onde um app pode organizar arquivos que têm valor para o usuário apenas dentro do seu app. Um diretório é criado para os arquivos persistentes do seu app e outro contém os arquivos armazenados em cache dele.
No Android 4.4 (API de nível 19) ou mais recentes, o app não precisa solicitar permissões relacionadas ao armazenamento para acessar diretórios específicos do app no armazenamento externo. Os arquivos armazenados nesses diretórios são removidos quando o app é desinstalado.
Em dispositivos com o Android 9 (nível 28 da API) ou versões anteriores, o app pode acessar os arquivos específicos que pertencem a outros apps, desde que ele tenha as permissões de armazenamento adequadas. Para dar mais controle aos usuários sobre os próprios arquivos e limitar a sobrecarga, os apps direcionados ao Android 10 (nível 29 da API) ou mais recentes recebem acesso com escopo ao armazenamento externo, ou ao armazenamento com escopo, por padrão. Quando o armazenamento com escopo está ativado, os apps não podem acessar os diretórios específicos do app que pertencem a outros apps.
Verificar se o armazenamento está disponível
Como o armazenamento externo reside em um volume físico que o usuário pode remover, verifique se o volume pode ser acessado antes de tentar ler ou gravar dados específicos do app no armazenamento externo.
É possível consultar o estado do volume chamando
Environment.getExternalStorageState()
.
Se o estado retornado for MEDIA_MOUNTED
, você poderá ler e gravar arquivos específicos do app no armazenamento externo. Se for MEDIA_MOUNTED_READ_ONLY
, os arquivos só poderão ser lidos.
Por exemplo, os métodos a seguir são úteis para determinar a disponibilidade do armazenamento:
Kotlin
// Checks if a volume containing external storage is available // for read and write. fun isExternalStorageWritable(): Boolean { return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED } // Checks if a volume containing external storage is available to at least read. fun isExternalStorageReadable(): Boolean { return Environment.getExternalStorageState() in setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY) }
Java
// Checks if a volume containing external storage is available // for read and write. private boolean isExternalStorageWritable() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } // Checks if a volume containing external storage is available to at least read. private boolean isExternalStorageReadable() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) || Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY); }
Em dispositivos sem armazenamento externo removível, use o seguinte comando para ativar um volume virtual e testar a lógica de disponibilidade do armazenamento externo:
adb shell sm set-virtual-disk true
Selecionar um local físico de armazenamento
Às vezes, um dispositivo que aloca uma partição da própria memória interna para uso como armazenamento externo também tem um slot para cartão SD. Isso significa que o dispositivo possui vários volumes físicos que podem conter armazenamento externo. Por isso, é necessário selecionar qual deles usar para o armazenamento específico do app.
Para acessar os diferentes locais, chame
ContextCompat.getExternalFilesDirs()
.
Como mostrado no snippet de código, o primeiro elemento na matriz retornada é considerado o volume de armazenamento externo principal. Use esse volume, a menos que ele esteja cheio ou indisponível.
Kotlin
val externalStorageVolumes: Array<out File> = ContextCompat.getExternalFilesDirs(applicationContext, null) val primaryExternalStorage = externalStorageVolumes[0]
Java
File[] externalStorageVolumes = ContextCompat.getExternalFilesDirs(getApplicationContext(), null); File primaryExternalStorage = externalStorageVolumes[0];
Acessar arquivos persistentes
Para acessar arquivos específicos do app no armazenamento externo, chame
getExternalFilesDir()
.
A fim de manter o bom desempenho do app, não abra e feche o mesmo arquivo várias vezes.
O snippet de código abaixo demonstra como chamar getExternalFilesDir()
:
Kotlin
val appSpecificExternalDir = File(context.getExternalFilesDir(null), filename)
Java
File appSpecificExternalDir = new File(context.getExternalFilesDir(null), filename);
Criar arquivos em cache
Para adicionar um arquivo específico do app ao cache no armazenamento externo, acesse
uma referência ao
externalCacheDir
:
Kotlin
val externalCacheFile = File(context.externalCacheDir, filename)
Java
File externalCacheFile = new File(context.getExternalCacheDir(), filename);
Remover arquivos em cache
Para remover um arquivo do diretório de cache externo, use o método
delete()
em um objeto File
que
representa o arquivo:
Kotlin
externalCacheFile.delete()
Java
externalCacheFile.delete();
Conteúdo de mídia
Se o app funcionar com arquivos de mídia que sejam úteis para o usuário apenas dentro dele, é recomendável armazená-los em diretórios específicos do app no armazenamento externo, conforme demonstrado no seguinte snippet de código:
Kotlin
fun getAppSpecificAlbumStorageDir(context: Context, albumName: String): File? { // Get the pictures directory that's inside the app-specific directory on // external storage. val file = File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName) if (!file?.mkdirs()) { Log.e(LOG_TAG, "Directory not created") } return file }
Java
@Nullable File getAppSpecificAlbumStorageDir(Context context, String albumName) { // Get the pictures directory that's inside the app-specific directory on // external storage. File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (file == null || !file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; }
É importante usar nomes de diretórios fornecidos por constantes da API, como
DIRECTORY_PICTURES
.
Esses nomes de diretório garantem que os arquivos sejam tratados de forma adequada pelo sistema.
Se nenhum dos nomes de subdiretórios predefinidos for adequado para seus arquivos, transmita null
para getExternalFilesDir()
. Isso retorna o diretório raiz específico do app no armazenamento externo.
Consultar espaço livre
Boa parte dos usuários não tem muito espaço de armazenamento disponível nos dispositivos. Por isso, o app precisa consumir espaço de modo cuidadoso.
Se você souber com antecedência a quantidade de dados que está armazenando, é possível
descobrir quanto espaço o dispositivo pode fornecer ao app chamando
getAllocatableBytes()
.
O valor de retorno de getAllocatableBytes()
pode ser maior do que a quantidade atual de espaço livre no dispositivo. Isso ocorre porque o sistema identificou
arquivos que podem ser removidos dos diretórios de cache de outros apps.
Se houver espaço suficiente para salvar os dados do app, chame
allocateBytes()
.
Caso contrário, o app pode solicitar que o usuário remova alguns
arquivos do dispositivo ou remova todos os arquivos
de cache do dispositivo.
O snippet de código abaixo mostra um exemplo de como o app pode consultar o espaço livre no dispositivo:
Kotlin
// App needs 10 MB within internal storage. const val NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L; val storageManager = applicationContext.getSystemService<StorageManager>()!! val appSpecificInternalDirUuid: UUID = storageManager.getUuidForPath(filesDir) val availableBytes: Long = storageManager.getAllocatableBytes(appSpecificInternalDirUuid) if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) { storageManager.allocateBytes( appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP) } else { val storageIntent = Intent().apply { // To request that the user remove all app cache files instead, set // "action" to ACTION_CLEAR_APP_CACHE. action = ACTION_MANAGE_STORAGE } }
Java
// App needs 10 MB within internal storage. private static final long NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L; StorageManager storageManager = getApplicationContext().getSystemService(StorageManager.class); UUID appSpecificInternalDirUuid = storageManager.getUuidForPath(getFilesDir()); long availableBytes = storageManager.getAllocatableBytes(appSpecificInternalDirUuid); if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) { storageManager.allocateBytes( appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP); } else { // To request that the user remove all app cache files instead, set // "action" to ACTION_CLEAR_APP_CACHE. Intent storageIntent = new Intent(); storageIntent.setAction(ACTION_MANAGE_STORAGE); }
Criar uma atividade de gerenciamento de armazenamento
Um app pode declarar e criar uma atividade personalizada que, quando iniciada, permite que
o usuário gerencie os dados que o app armazenou no dispositivo. Para
declarar uma atividade personalizada "gerenciar espaço", os apps usam o atributo
android:manageSpaceActivity
no arquivo de manifesto. Os apps gerenciadores de arquivos podem invocar essa
atividade
mesmo quando o app não a exporta, ou seja, quando a atividade define
android:exported
como
false
.
Pedir que o usuário remova alguns arquivos do dispositivo
Para solicitar que o usuário selecione alguns arquivos no dispositivo que vão ser removidos, invoque uma intent
que inclua
a ação
ACTION_MANAGE_STORAGE
. Essa intent exibe uma solicitação ao usuário. Essa solicitação pode
exibir a quantidade de espaço livre disponível no dispositivo, se você quiser. Para mostrar essas
informações úteis, use o resultado do cálculo abaixo:
StorageStatsManager.getFreeBytes() / StorageStatsManager.getTotalBytes()
Pedir que o usuário remova todos os arquivos em cache
Também é possível solicitar que o usuário limpe os arquivos de cache de todos
os apps no dispositivo. Para fazer isso, invoque uma intent que contenha a
ação de intent
ACTION_CLEAR_APP_CACHE
.
Outros recursos
Para mais informações sobre como salvar arquivos no armazenamento do dispositivo, consulte os seguintes recursos.