这一节主要了解一下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.preferencesDataStore
private 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.map
class 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] = name
preferences[PreferencesKeys.USER_AGE] = age
preferences[PreferencesKeys.IS_PREMIUM_USER] = isPremium
preferences[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.current
val 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:这同样可能会导致数据不一致或冲突。