DataStore
1. Giới thiệu
Phần tiêu đề “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
Phần tiêu đề “So sánh với SharedPreferences”| Feature | SharedPreferences | DataStore |
|---|---|---|
| Async API | ❌ | ✅ |
| Main thread safe | ❌ | ✅ |
| Error handling | ❌ | ✅ |
| Type safety | ❌ | ✅ (Proto) |
| Migration | ❌ | ✅ |
2. Setup
Phần tiêu đề “2. Setup”dependencies { implementation("androidx.datastore:datastore-preferences:1.1.1")}3. Preferences DataStore
Phần tiêu đề “3. Preferences DataStore”Tạo DataStore
Phần tiêu đề “Tạo DataStore”val Context.dataStore: DataStore<Preferences> by preferencesDataStore( name = "settings")Định nghĩa Keys
Phần tiêu đề “Đị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
Phần tiêu đề “Đọ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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “5. Sử dụng với Compose”@Composablefun 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
Phần tiêu đề “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
Phần tiêu đề “7. Migration từ SharedPreferences”val Context.dataStore by preferencesDataStore( name = "settings", produceMigrations = { context -> listOf(SharedPreferencesMigration(context, "old_prefs_name")) })8. Proto DataStore (Type-safe)
Phần tiêu đề “8. Proto DataStore (Type-safe)”Setup
Phần tiêu đề “Setup”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
Phần tiêu đề “Định nghĩa Proto”syntax = "proto3";
message Settings { string username = 1; int32 age = 2; bool is_logged_in = 3;}Serializer
Phần tiêu đề “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
Phần tiêu đề “Sử dụng Proto DataStore”val Context.settingsDataStore: DataStore<Settings> by dataStore( fileName = "settings.pb", serializer = SettingsSerializer)
// Readval settingsFlow: Flow<Settings> = context.settingsDataStore.data
// Writesuspend fun updateUsername(username: String) { context.settingsDataStore.updateData { current -> current.toBuilder() .setUsername(username) .build() }}📝 Tóm tắt
Phần tiêu đề “📝 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 |