DataStore trong Android
1. Giới thiệu
DataStore là giải pháp lưu trữ data thay thế SharedPreferences, an toàn với coroutines và Flow.
So sánh với SharedPreferences
| Feature | SharedPreferences | DataStore |
|---|---|---|
| Async API | ❌ | ✅ |
| Main thread safe | ❌ | ✅ |
| Error handling | ❌ | ✅ |
| Type safety | ❌ | ✅ (Proto) |
| Migration | ❌ | ✅ |
2. Setup
// build.gradle.kts
dependencies {
implementation("androidx.datastore:datastore-preferences:1.1.1")
}3. Preferences DataStore
Tạo DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = "settings"
)Định nghĩa Keys
object PreferencesKeys {
val USERNAME = stringPreferencesKey("username")
val AGE = intPreferencesKey("age")
val IS_LOGGED_IN = booleanPreferencesKey("is_logged_in")
val SCORE = floatPreferencesKey("score")
}Đọc dữ liệu
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
)
}
}Ghi dữ liệu
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
}
}4. Sử dụng với ViewModel
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)
}
}
}5. Sử dụng với Compose
@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")
}
}
}6. Error Handling
val settingsFlow: Flow<UserSettings> = context.dataStore.data
.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preferences ->
UserSettings(...)
}7. Migration từ SharedPreferences
val Context.dataStore by preferencesDataStore(
name = "settings",
produceMigrations = { context ->
listOf(SharedPreferencesMigration(context, "old_prefs_name"))
}
)8. Proto DataStore (Type-safe)
Setup
// 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")
}Định nghĩa Proto
// src/main/proto/settings.proto
syntax = "proto3";
message Settings {
string username = 1;
int32 age = 2;
bool is_logged_in = 3;
}Serializer
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)
}
}Sử dụng Proto DataStore
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()
}
}📝 Tóm tắt
| 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 |
Last updated on