这一节主要了解一下Compose中的DataStore,在Jetpack Compose中,DataStore是一种数据存储解决方案,用于以异步、一致的方式存储键值对或自定义类型的数据。它提供了替代SharedPreferences的更现代化、更安全的方案,也是一个基于Flow和Coroutines的异步数据存储解决方案。
作用: 1 异步操作:避免在主线程上进行阻塞式的 I/O 操作,确保应用的流畅性。2 类型安全:Proto DataStore可以存储自定义的Protocol Buffers消息,提供类型安全的访问。 3 一致性:保证数据的一致性,避免了SharedPreferences可能出现的并发问题。
栗子:
添加依赖:
implementation("androidx.datastore:datastore-preferences:1.1.3")
import android.content.Context
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStoreprivate const val PREFERENCES_NAME = "complex_preferences"private val Context.dataStore by preferencesDataStore(name = PREFERENCES_NAME)object PreferencesKeys {val USER_NAME = stringPreferencesKey("user_name")val USER_AGE = intPreferencesKey("user_age")val IS_PREMIUM_USER = booleanPreferencesKey("is_premium_user")val FAVORITE_COLORS = stringSetPreferencesKey("favorite_colors")
}
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.mapclass PreferencesManager(private val context: Context) {val userNameFlow: Flow<String?> = context.dataStore.data.map { preferences ->preferences[PreferencesKeys.USER_NAME]}val userAgeFlow: Flow<Int?> = context.dataStore.data.map { preferences ->preferences[PreferencesKeys.USER_AGE]}val isPremiumUserFlow: Flow<Boolean?> = context.dataStore.data.map { preferences ->preferences[PreferencesKeys.IS_PREMIUM_USER]}val favoriteColorsFlow: Flow<Set<String>?> = context.dataStore.data.map { preferences ->preferences[PreferencesKeys.FAVORITE_COLORS]}suspend fun saveUserName(name: String) {context.dataStore.edit { preferences ->preferences[PreferencesKeys.USER_NAME] = name}}suspend fun saveUserAge(age: Int) {context.dataStore.edit { preferences ->preferences[PreferencesKeys.USER_AGE] = age}}suspend fun saveIsPremiumUser(isPremium: Boolean) {context.dataStore.edit { preferences ->preferences[PreferencesKeys.IS_PREMIUM_USER] = isPremium}}suspend fun saveFavoriteColors(colors: Set<String>) {context.dataStore.edit { preferences ->preferences[PreferencesKeys.FAVORITE_COLORS] = colors}}suspend fun batchUpdatePreferences(name: String, age: Int, isPremium: Boolean, colors: Set<String>) {context.dataStore.edit { preferences ->preferences[PreferencesKeys.USER_NAME] = namepreferences[PreferencesKeys.USER_AGE] = agepreferences[PreferencesKeys.IS_PREMIUM_USER] = isPremiumpreferences[PreferencesKeys.FAVORITE_COLORS] = colors}}suspend fun clearAllPreferences() {context.dataStore.edit { preferences ->preferences.clear()}}
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch@Composable
fun TestPreferencesScreen() {val context = LocalContext.currentval preferencesManager = remember { PreferencesManager(context) }val scope = rememberCoroutineScope()val userName by preferencesManager.userNameFlow.collectAsState(initial = null)val userAge by preferencesManager.userAgeFlow.collectAsState(initial = null)val isPremiumUser by preferencesManager.isPremiumUserFlow.collectAsState(initial = null)val favoriteColors by preferencesManager.favoriteColorsFlow.collectAsState(initial = null)Column(modifier = Modifier.fillMaxSize().padding(16.dp),verticalArrangement = Arrangement.spacedBy(16.dp),horizontalAlignment = Alignment.CenterHorizontally) {Text(text = "User Name: ${userName ?: "Not set"}")Text(text = "User Age: ${userAge ?: "Not set"}")Text(text = "Is Premium User: ${isPremiumUser ?: "Not set"}")Text(text = "Favorite Colors: ${favoriteColors?.joinToString() ?: "Not set"}")Button(onClick = {scope.launch {preferencesManager.saveUserName("John Doe")}}) {Text(text = "Save User Name")}Button(onClick = {scope.launch {preferencesManager.saveUserAge(30)}}) {Text(text = "Save User Age")}Button(onClick = {scope.launch {preferencesManager.saveIsPremiumUser(true)}}) {Text(text = "Save Is Premium User")}Button(onClick = {scope.launch {preferencesManager.saveFavoriteColors(setOf("Red", "Blue", "Green"))}}) {Text(text = "Save Favorite Colors")}Button(onClick = {scope.launch {preferencesManager.batchUpdatePreferences(name = "Jane Smith",age = 25,isPremium = false,colors = setOf("Yellow", "Purple"))}}) {Text(text = "Batch Update Preferences")}Button(onClick = {scope.launch {preferencesManager.clearAllPreferences()}}) {Text(text = "Clear All Preferences")}}
}
分析:TestPreferencesScreen 组件展示了如何在 Compose 中使用 PreferencesManager 来读取和保存偏好设置。通过 collectAsState 方法将 Flow 转换为可观察的状态,当偏好设置发生变化时,界面会自动更新。同时,提供了多个按钮来触发不同的保存和更新操作。
注意:
1 异步操作:DataStore 的所有读写操作都是异步的,需要在协程中进行。在 Compose 中,可以使用rememberCoroutineScope来创建协程作用域。
2 数据迁移:如果从SharedPreferences迁移到 DataStore,需要手动实现数据迁移逻辑。
3 避免在同一进程中为给定文件创建多个DataStore实例:这可能会导致数据不一致或冲突。
4 DataStore的通用类型必须是不可变的:这有助于确保数据的完整性和一致性。
5 不要为同一文件混合使用SingleProcessDataStore和MultiProcessDataStore:这同样可能会导致数据不一致或冲突。