
Jetpack DataStore no Android com Kotlin: A Evolução do SharedPreferences
O Jetpack DataStore surge como a evolução do tradicional SharedPreferences, trazendo uma abordagem mais segura, moderna e assíncrona para armazenamento de dados no Android. Diferente da API antiga, o DataStore utiliza Kotlin Coroutines e Flow, evitando problemas comuns como bloqueio de thread e inconsistência em acessos concorrentes. Neste tutorial, você vai entender as principais mudanças, vantagens e quando faz sentido migrar para essa nova solução em projetos reais.
Publicado em 12 de outubro de 2025 às 17:03
Durante anos, o SharedPreferences foi a solução padrão para armazenamento simples no Android. Ele resolve bem problemas básicos, mas carrega limitações importantes quando pensamos em aplicações modernas.
Para resolver essas limitações, o Android introduziu o Jetpack DataStore, uma API mais segura, assíncrona e alinhada com boas práticas atuais.
Neste artigo, você vai entender:
por que o DataStore foi criado
quais problemas ele resolve
como utilizá-lo com Kotlin
como tomar decisões reais em projetos
Por que o DataStore existe?
O SharedPreferences tem alguns problemas estruturais:
Escrita síncrona (pode travar a UI)
Falta de segurança contra concorrência
API baseada em XML
Sem suporte nativo a reatividade
Em projetos pequenos isso passa despercebido.
Em sistemas reais, isso vira dor de cabeça.
O DataStore foi criado para resolver exatamente isso.
Principais mudanças (SharedPreferences vs DataStore)
A principal mudança entre o SharedPreferences e o DataStore vai muito além da API em si (ela está na forma como lidamos com dados dentro da aplicação). Enquanto o SharedPreferences permite leituras diretas e gravações que podem ser síncronas ou mal controladas, o DataStore adota uma abordagem totalmente assíncrona baseada em coroutines, evitando bloqueios na thread principal. Além disso, o modelo de leitura muda de imperativo para reativo: em vez de simplesmente buscar um valor, você passa a observar um fluxo de dados com Flow, reagindo automaticamente às mudanças. Outro ponto importante é a concorrência (no SharedPreferences, múltiplas escritas podem gerar inconsistência, enquanto o DataStore garante consistência transacional). Em termos de estrutura, o SharedPreferences utiliza arquivos XML, enquanto o DataStore trabalha com um modelo mais moderno, podendo ser chave-valor (Preferences) ou tipado (Proto). Por fim, o tratamento de erros e a integração com arquiteturas modernas também são muito mais robustos no DataStore, tornando-o uma escolha mais adequada para aplicações atuais.
A grande mudança aqui não é só técnica, é arquitetural.
Tipos de DataStore
O DataStore possui dois modos:
Preferences DataStore
Similar ao SharedPreferences
Chave-valor
Mais simples
Proto DataStore
Usa Protobuf
Tipado (forte)
Melhor para sistemas maiores
Para começar, o mais comum é usar o Preferences DataStore.
Configurando o DataStore
Dependência
implementation("androidx.datastore:datastore-preferences:1.0.0")Criando a instância
val Context.dataStore by preferencesDataStore(name = "settings")Gravando dados
val USERNAME = stringPreferencesKey("username")
suspend fun saveUsername(context: Context, name: String) {
context.dataStore.edit { prefs ->
prefs[USERNAME] = name
}
}Note que:
é suspend
usa coroutine
não bloqueia thread
Lendo dados (reativo)
fun getUsername(context: Context): Flow<String?> {
return context.dataStore.data
.map { prefs ->
prefs[USERNAME]
}
}Aqui está o grande diferencial:
você não “busca” o valor
você observa mudanças
Consumindo no ViewModel
viewModelScope.launch {
getUsername(context).collect { username ->
println("Usuário: $username")
}
}Isso permite:
UI reativa
integração com StateFlow
arquitetura moderna
Problemas que o DataStore resolve
Travamentos na UI
SharedPreferences pode bloquear a thread principal.
DataStore resolve com coroutines.
Concorrência
SharedPreferences pode gerar inconsistência.
DataStore garante consistência transacional.
Falta de reatividade
SharedPreferences não notifica mudanças.
DataStore usa Flow.
Boas práticas (nível profissional)
Criar uma camada de acesso
class UserPreferences(private val context: Context) {
companion object {
val USERNAME = stringPreferencesKey("username")
}
val usernameFlow: Flow<String?> =
context.dataStore.data.map { it[USERNAME] }
suspend fun saveUsername(name: String) {
context.dataStore.edit {
it[USERNAME] = name
}
}
}Nunca acessar direto da UI
Errado:
context.dataStore...Correto:
usar Repository ou UseCase.
Tratar exceções
context.dataStore.data
.catch { exception ->
emit(emptyPreferences())
}Segurança
O DataStore não é criptografado por padrão.
Se precisar de segurança:
combine com criptografia
ou use soluções específicas de armazenamento seguro
Quando usar DataStore?
Use quando:
precisa de reatividade
quer arquitetura moderna
precisa evitar problemas de concorrência
Evite quando:
precisa de consultas complexas → use banco de dados (Room)
grandes volumes de dados
Migração do SharedPreferences
Você pode migrar assim:
val Context.dataStore by preferencesDataStore(
name = "settings",
produceMigrations = { context ->
listOf(SharedPreferencesMigration(context, "old_prefs"))
}
)Isso permite migração automática.
Conclusão
O DataStore não é apenas uma nova API.
Ele representa uma mudança de mentalidade:
de síncrono para assíncrono
de leitura direta para reatividade
de simples para robusto
Se você está começando um projeto hoje:
não use SharedPreferences
prefira DataStore