Bỏ qua để đến nội dung

DataStore

DataStore là giải pháp lưu trữ data thay thế SharedPreferences, an toàn với coroutines và Flow.

Feature SharedPreferences DataStore
Async API
Main thread safe
Error handling
Type safety ✅ (Proto)
Migration
build.gradle.kts
dependencies {
implementation("androidx.datastore:datastore-preferences:1.1.1")
}
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = "settings"
)
object PreferencesKeys {
val USERNAME = stringPreferencesKey("username")
val AGE = intPreferencesKey("age")
val IS_LOGGED_IN = booleanPreferencesKey("is_logged_in")
val SCORE = floatPreferencesKey("score")
}
class SettingsRepository(private val context: Context) {
val usernameFlow: Flow<String> = context.dataStore.data
.map { preferences ->
preferences[PreferencesKeys.USERNAME] ?: ""
}
val settingsFlow: Flow<UserSettings> = context.dataStore.data
.map { preferences ->
UserSettings(
username = preferences[PreferencesKeys.USERNAME] ?: "",
age = preferences[PreferencesKeys.AGE] ?: 0,
isLoggedIn = preferences[PreferencesKeys.IS_LOGGED_IN] ?: false
)
}
}
suspend fun saveUsername(username: String) {
context.dataStore.edit { preferences ->
preferences[PreferencesKeys.USERNAME] = username
}
}
suspend fun saveSettings(settings: UserSettings) {
context.dataStore.edit { preferences ->
preferences[PreferencesKeys.USERNAME] = settings.username
preferences[PreferencesKeys.AGE] = settings.age
preferences[PreferencesKeys.IS_LOGGED_IN] = settings.isLoggedIn
}
}
class SettingsViewModel(
private val repository: SettingsRepository
) : ViewModel() {
val settings: StateFlow<UserSettings> = repository.settingsFlow
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = UserSettings()
)
fun updateUsername(username: String) {
viewModelScope.launch {
repository.saveUsername(username)
}
}
}
@Composable
fun SettingsScreen(viewModel: SettingsViewModel = viewModel()) {
val settings by viewModel.settings.collectAsState()
Column {
Text("Username: ${settings.username}")
var newUsername by remember { mutableStateOf("") }
OutlinedTextField(
value = newUsername,
onValueChange = { newUsername = it },
label = { Text("New Username") }
)
Button(onClick = { viewModel.updateUsername(newUsername) }) {
Text("Save")
}
}
}
val settingsFlow: Flow<UserSettings> = context.dataStore.data
.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preferences ->
UserSettings(...)
}
val Context.dataStore by preferencesDataStore(
name = "settings",
produceMigrations = { context ->
listOf(SharedPreferencesMigration(context, "old_prefs_name"))
}
)
build.gradle.kts
plugins {
id("com.google.protobuf") version "0.9.4"
}
dependencies {
implementation("androidx.datastore:datastore:1.1.1")
implementation("com.google.protobuf:protobuf-javalite:3.25.1")
}
src/main/proto/settings.proto
syntax = "proto3";
message Settings {
string username = 1;
int32 age = 2;
bool is_logged_in = 3;
}
object SettingsSerializer : Serializer<Settings> {
override val defaultValue: Settings = Settings.getDefaultInstance()
override suspend fun readFrom(input: InputStream): Settings {
return Settings.parseFrom(input)
}
override suspend fun writeTo(t: Settings, output: OutputStream) {
t.writeTo(output)
}
}
val Context.settingsDataStore: DataStore<Settings> by dataStore(
fileName = "settings.pb",
serializer = SettingsSerializer
)
// Read
val settingsFlow: Flow<Settings> = context.settingsDataStore.data
// Write
suspend fun updateUsername(username: String) {
context.settingsDataStore.updateData { current ->
current.toBuilder()
.setUsername(username)
.build()
}
}

Type Use Case
Preferences DataStore Key-value đơn giản
Proto DataStore Type-safe, complex objects
API Mô tả
dataStore.data Flow of preferences
dataStore.edit {} Modify data
map {} Transform data
catch {} Handle errors