Jetpack DataStore no Android com Kotlin: A Evolução do SharedPreferences
← Voltar para o blog
Kotlin
4029 views5 min de leitura

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