Scoped Storage é um modelo de acesso ao sistema de arquivos introduzido no Android 10 (API 29) que restringe o acesso arbitrário de aplicativos ao armazenamento compartilhado do dispositivo. De acordo com a Documentação para Desenvolvedores Android do Google (2024), o Scoped Storage substitui o antigo modelo de permissões READ_EXTERNAL_STORAGE e WRITE_EXTERNAL_STORAGE, concedendo aos aplicativos acesso apenas aos seus próprios arquivos em uma sandbox isolada e a coleções compartilhadas através do MediaStore. Esse modelo aumenta a privacidade dos dados do usuário e previne a coleta não autorizada de informações por aplicativos.
Principais pontos
Scoped Storage é uma mudança arquitetural no Android 10 (API 29) que transforma fundamentalmente a forma como os aplicativos acessam o sistema de arquivos do dispositivo. Em vez de acesso total ao armazenamento compartilhado, o aplicativo obtém acesso apenas à sua própria sandbox e a coleções de mídia específicas através do MediaStore.
Antes do Scoped Storage, qualquer aplicativo com permissão READ_EXTERNAL_STORAGE podia ler todos os arquivos no armazenamento compartilhado — fotos, documentos, downloads, dados de outros aplicativos. Isso criava riscos de privacidade: aplicativos não confiáveis podiam coletar informações do usuário sem seu conhecimento.
De acordo com o Google I/O 2019, mais de 65% dos usuários do Android consideram a privacidade dos dados um fator crítico ao escolher um aplicativo. O Scoped Storage é uma resposta direta a essa demanda: cada aplicativo opera em um ambiente isolado e obtém acesso aos dados de outros aplicativos apenas com o consentimento explícito do usuário.
Importante: o Scoped Storage no Android 10 era opcional (o aplicativo podia desativá-lo através do requestLegacyExternalStorage). A partir do Android 11 (API 30), o Scoped Storage tornou-se obrigatório para todos os aplicativos, independentemente do target SDK.
O Google introduziu o Scoped Storage para resolver três problemas fundamentais do antigo modelo de acesso a arquivos: privacidade, gerenciamento de permissões e limpeza de arquivos residuais.
No modelo antigo, um aplicativo com READ_EXTERNAL_STORAGE podia escanear todo o armazenamento compartilhado e coletar metadados dos arquivos do usuário — coordenadas geográficas de fotos, nomes de documentos, estrutura de diretórios. O Scoped Storage elimina essa possibilidade: mesmo com permissão, o aplicativo vê apenas os arquivos que ele mesmo criou e os arquivos selecionados explicitamente pelo usuário através do SAF.
Antes do Scoped Storage, os aplicativos podiam deixar arquivos no armazenamento compartilhado após a desinstalação. Com o tempo, acumulavam-se diretórios lixo. O Scoped Storage resolve isso: todos os arquivos na sandbox do aplicativo são removidos junto com o aplicativo, e os arquivos através do MediaStore têm um proprietário e podem ser limpos pelo sistema.
| Aspecto | Modelo antigo (Legacy) | Scoped Storage |
|---|---|---|
| Acesso ao armazenamento compartilhado | Total (com permissão) | Apenas sua sandbox |
| Arquivos de mídia | Caminho de arquivo direto | Através de URI do MediaStore |
| Arquivos de outros aplicativos | Sempre acessíveis | Apenas através do SAF |
| Limpeza ao desinstalar | Arquivos permanecem | Sandbox é removida |
| Controle do usuário | Mínimo | Consentimento explícito por arquivo |
O Scoped Storage faz parte da estratégia geral de privacidade do Google no Android, que também inclui permissões "apenas enquanto em uso", indicadores de acesso à câmera e microfone e o Painel de Privacidade.
Cada aplicativo no Android recebe seu próprio diretório privado, acessível através de Context.getFilesDir() e context.getCacheDir(). Para acessar o diretório privado externo (compartilhado), é usado Context.getExternalFilesDir().
No Scoped Storage, um aplicativo tem acesso total ao seu diretório privado externo sem nenhuma permissão. Este é o local principal para armazenar arquivos que não devem ser acessíveis a outros aplicativos ou que o aplicativo cria para seu próprio uso.
import android.os.Environment
import java.io.File
import java.io.IOException
class FileManager {
fun saveToAppStorage(context: Context, fileName: String, data: ByteArray) {
val appDir = context.getExternalFilesDir(null)
?: return
val file = File(appDir, fileName)
file.writeBytes(data)
}
fun readFromAppStorage(context: Context, fileName: String): ByteArray? {
val appDir = context.getExternalFilesDir(null)
?: return null
val file = File(appDir, fileName)
return file.takeIf { it.exists() }?.readBytes()
}
}
Caminho para o diretório privado externo: /storage/emulated/0/Android/data/{packageName}/files/. A partir do Android 11, o caminho de arquivo direto não está disponível — apenas através da API. Esta é mais uma melhoria de segurança do Scoped Storage.
MediaStore é a API principal para acessar arquivos de mídia compartilhados (imagens, vídeos, áudio) no Scoped Storage. O aplicativo não trabalha com caminhos de arquivo, mas com content URIs fornecidos pelo provedor MediaStore.
O MediaStore é dividido em três coleções principais: Images, Video e Audio. Cada coleção suporta operações CRUD através do ContentResolver. Para inserir um novo arquivo na coleção Images, é usado MediaStore.Images.Media; para consultar os existentes — query com a URI correspondente.
import android.content.ContentValues
import android.provider.MediaStore
import android.os.Environment
import java.io.OutputStream
fun saveImageToGallery(context: Context, bitmap: Bitmap, title: String) {
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "$title.jpg")
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(
MediaStore.Images.Media.RELATIVE_PATH,
"${Environment.DIRECTORY_PICTURES}/MyApp"
)
}
val uri = context.contentResolver
.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
?: return
context.contentResolver.openOutputStream(uri)?.use { output: OutputStream ->
bitmap.compress(
Bitmap.CompressFormat.JPEG,
95,
output
)
}
}
Importante: o MediaStore não suporta caminhos de arquivo arbitrários — apenas content URIs. Tentar obter um caminho de arquivo de uma URI através de MediaStore.Files.getContentUri() não dará um caminho direto para o arquivo no Android 11+. Em vez disso, use ContentResolver.openInputStream() e openOutputStream() para trabalhar com o conteúdo.
Storage Access Framework (SAF) é uma API para acessar arquivos e diretórios arbitrários fora da sandbox do aplicativo. O SAF fornece ao usuário uma interface de seleção de arquivo ou diretório, após a qual o aplicativo recebe um content URI com acesso temporário.
O SAF é usado para cenários não cobertos pelo MediaStore: trabalhar com documentos arbitrários (PDF, ZIP, APK), acessar diretórios no cartão SD, importar e exportar arquivos de outros aplicativos. O usuário seleciona explicitamente um arquivo através do seletor de arquivos do sistema — isso garante seu consentimento para o acesso.
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.provider.DocumentsContract
const val REQUEST_CODE_PICK_DIR = 1001
fun pickDirectory(activity: Activity) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
activity.startActivityForResult(intent, REQUEST_CODE_PICK_DIR)
}
fun handlePickResult(requestCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE_PICK_DIR && data != null) {
val treeUri: Uri = data.data ?: return
// Take persistent URI permission
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(treeUri, flags)
}
}
O SAF concede ao aplicativo acesso ao diretório selecionado pelo usuário até a reinicialização do dispositivo (ao usar takePersistableUriPermission). Esta é a única maneira de obter acesso a arquivos arbitrários no armazenamento compartilhado no Android 11+.
Migrar um aplicativo Android existente para o Scoped Storage requer mudanças em várias áreas principais. O Google recomenda uma abordagem em fases com testes no Android 11+.
Substitua todas as operações diretas com File no armazenamento compartilhado por ContentResolver.openInputStream / openOutputStream. Para arquivos na sandbox do aplicativo (getExternalFilesDir), os caminhos de arquivo continuam funcionando.
Remova READ_EXTERNAL_STORAGE e WRITE_EXTERNAL_STORAGE se não forem necessários para cenários específicos (por exemplo, gerenciamento de arquivos — para isso existe a permissão especial MANAGE_EXTERNAL_STORAGE).
| Cenário | Abordagem antiga | Abordagem Scoped Storage |
|---|---|---|
| Salvar uma foto | File(path).writeBytes() | MediaStore + ContentResolver |
| Ler PDF | File(path).inputStream() | SAF ACTION_OPEN_DOCUMENT |
| Arquivos próprios | Environment.getExternalStorageDirectory() | context.getExternalFilesDir() |
| Cache | File(cacheDir).writeBytes() | context.cacheDir (sem alterações) |
Após a migração, teste o aplicativo no Android 11+ (API 30), definindo targetSdk = 30 ou superior no build.gradle. Certifique-se de que todas as operações com arquivos no armazenamento compartilhado funcionem através do MediaStore ou SAF, e não através de caminhos de arquivo diretos.
import android.os.Build
fun isScopedStorage(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
}
fun compatReadFile(context: Context, uri: Uri): ByteArray? {
return if (isScopedStorage()) {
context.contentResolver
.openInputStream(uri)?.readBytes()
} else {
File(uri.path ?: return null)
.takeIf { it.exists() }
?.readBytes()
}
}
De acordo com o Google Play Console (2024), mais de 78% dos dispositivos Android ativos executam Android 10+ com Scoped Storage. A migração é uma etapa obrigatória para publicar atualizações no Google Play: novos aplicativos devem ter target API 31+, atualizações — API 30+.
Perguntas frequentes
Não, a partir do Android 11 (API 30) o Scoped Storage é obrigatório para todos os aplicativos. O flag requestLegacyExternalStorage, disponível no Android 10, não funciona na API 30+. A única maneira de obter acesso amplo ao sistema de arquivos é a permissão MANAGE_EXTERNAL_STORAGE, mas ela é destinada apenas para gerenciadores de arquivos e antivírus.
MANAGE_EXTERNAL_STORAGE é uma permissão especial para aplicativos que precisam de acesso total ao sistema de arquivos (gerenciadores de arquivos, ferramentas de backup). A solicitação de permissão abre uma tela do sistema com um aviso para o usuário. Ao publicar no Google Play, é necessário preencher um formulário de Declaração sobre a necessidade dessa permissão.
No Scoped Storage no Android 11+, não é possível obter um caminho de arquivo direto de um content URI. Em vez disso, use ContentResolver.openInputStream() para leitura e openOutputStream() para escrita. Se precisar de um caminho de arquivo para compatibilidade com uma biblioteca de terceiros, crie uma cópia do arquivo em getCacheDir() e trabalhe com a cópia.
O FileProvider continua funcionando sem alterações — ele é usado para fornecer acesso a arquivos da sandbox do aplicativo a outros aplicativos através de content URIs. O Scoped Storage não afeta o FileProvider, pois o FileProvider funciona no nível de content URIs, não de caminhos de arquivo diretos.
Verifique Build.VERSION.SDK_INT: se for >= Build.VERSION_CODES.Q (29), então o dispositivo suporta Scoped Storage. No entanto, no Android 10, o Scoped Storage pode ser desativado através de requestLegacyExternalStorage. No Android 11+, verifique se o flag de manifesto requestLegacyExternalStorage está definido — se não, o Scoped Storage está ativo.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também