💻 Exemplos: Capítulo 14: Armazenamento com Preferences DataStore
Exemplos de Código: Capítulo 14 — Armazenamento com Jetpack DataStore
Especialização em Mobile Nativo com Android • Demonstrações Práticas Compiláveis
💻 Código de Demonstração: Preferences DataStore, Chaves Fortemente Tipadas e Leitura Reativa
O código abaixo demonstra a substituição moderna e segura do legado SharedPreferences pela biblioteca Jetpack DataStore:
- Leitura não-bloqueante através de Kotlin Coroutines e
Flow. - Garantia de consistência transacional e isolamento contra corrupção de arquivos via
dataStore.edit {}. - Tipagem estrita de chaves com
stringPreferencesKey,booleanPreferencesKeyeintPreferencesKey.
package br.com.portal.mobile.android.cap14
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
// =====================================================================
// 1. Configuração e Chaves do DataStore (Contratos de Preferência)
// =====================================================================
/*
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import java.io.IOException
// Extensão delegada criando uma única instância por processo
val Context.configDataStore: DataStore<Preferences> by preferencesDataStore(name = "portal_config")
*/
data class ConfiguracoesUsuario(
val modoEscuroAtivo: Boolean,
val tokenAutenticacao: String?,
val idiomaSelecionado: String
)
// =====================================================================
// 2. Repositório de Preferências baseado em DataStore
// =====================================================================
class ConfiguracoesRepository(
// private val dataStore: DataStore<Preferences>
) {
/*
private object Chaves {
val MODO_ESCURO = booleanPreferencesKey("chave_modo_escuro")
val TOKEN_JWT = stringPreferencesKey("chave_token_jwt")
val IDIOMA = stringPreferencesKey("chave_idioma")
}
// Leitura contínua e reativa via Flow (Lança emptyPreferences em falhas I/O)
val configuracoesFlow: Flow<ConfiguracoesUsuario> = dataStore.data
.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preferences ->
ConfiguracoesUsuario(
modoEscuroAtivo = preferences[Chaves.MODO_ESCURO] ?: false,
tokenAutenticacao = preferences[Chaves.TOKEN_JWT],
idiomaSelecionado = preferences[Chaves.IDIOMA] ?: "pt-BR"
)
}
// Gravação assíncrona atômica
suspend fun salvarModoEscuro(ativo: Boolean) {
dataStore.edit { preferences ->
preferences[Chaves.MODO_ESCURO] = ativo
}
}
suspend fun salvarTokenAutenticacao(token: String) {
dataStore.edit { preferences ->
preferences[Chaves.TOKEN_JWT] = token
}
}
suspend fun limparSessao() {
dataStore.edit { preferences ->
preferences.remove(Chaves.TOKEN_JWT)
}
}
*/
}
// =====================================================================
// 3. Emulador de Teste de DataStore em Memória
// =====================================================================
class FakeDataStore {
private val dados = MutableStateFlow(
ConfiguracoesUsuario(
modoEscuroAtivo = false,
tokenAutenticacao = null,
idiomaSelecionado = "pt-BR"
)
)
val streamConfiguracoes: Flow<ConfiguracoesUsuario> = dados
suspend fun atualizarModoEscuro(ativo: Boolean) {
dados.value = dados.value.copy(modoEscuroAtivo = ativo)
}
suspend fun autenticar(token: String) {
dados.value = dados.value.copy(tokenAutenticacao = token)
}
}
fun main() = runBlocking {
println("=== GERENCIAMENTO DE ESTADO COM DATASTORE PREFERENCES ===")
val fakeStorage = FakeDataStore()
println("[1] Estado Inicial Padrão:")
println(" -> Modo Escuro: ${fakeStorage.streamConfiguracoes.map { it.modoEscuroAtivo }}")
// Atualização de configurações
fakeStorage.atualizarModoEscuro(true)
fakeStorage.autenticar("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
fakeStorage.streamConfiguracoes.collect { config ->
println("\n[2] Preferências Atualizadas via Coroutine:")
println(" -> Modo Escuro Ativo: ${config.modoEscuroAtivo}")
println(" -> Token Autenticado: ${config.tokenAutenticacao?.take(20)}...")
println(" -> Idioma do App: ${config.idiomaSelecionado}")
return@collect
}
}