Jetpack Compose多关卡Quiz应用:Preference Datastore存档关卡失效求助
基于Jetpack Compose的Quiz应用关卡持久化解决方案(Preference Datastore版)
一、正确的Preference Datastore实现
确保Datastore类遵循单例原则,读写逻辑规范:
import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.preferencesDataStore import android.content.Context import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map // 全局唯一的Datastore实例,通过Context扩展属性创建 val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "quiz_prefs") class QuizDataStore(private val context: Context) { // 定义存储当前关卡的Key private val CURRENT_LEVEL = intPreferencesKey("current_level") // 存储关卡:suspend函数,必须在协程中调用 suspend fun saveCurrentLevel(level: Int) { context.dataStore.edit { prefs -> prefs[CURRENT_LEVEL] = level } } // 获取关卡:返回Flow,Compose可通过collectAsState监听 fun getCurrentLevel(): Flow<Int> { return context.dataStore.data .map { prefs -> // 未存储过则默认返回第一关 prefs[CURRENT_LEVEL] ?: 1 } } }
二、Compose中结合ViewModel使用
推荐用ViewModel封装数据逻辑,避免在Composable中直接操作Datastore:
1. 创建ViewModel
import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch class QuizViewModel(private val dataStore: QuizDataStore) : ViewModel() { // 暴露给Compose的关卡Flow val currentLevel = dataStore.getCurrentLevel() // 对外提供保存关卡的方法 fun saveLevel(level: Int) { viewModelScope.launch { dataStore.saveCurrentLevel(level) } } } // ViewModel工厂类,用于注入QuizDataStore class QuizViewModelFactory(private val dataStore: QuizDataStore) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(QuizViewModel::class.java)) { return QuizViewModel(dataStore) as T } throw IllegalArgumentException("未知的ViewModel类") } }
2. 在Compose界面中使用
import androidx.compose.foundation.layout.* import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun QuizHomeScreen() { val context = LocalContext.current // 创建ViewModel实例 val quizViewModel: QuizViewModel = viewModel( factory = QuizViewModelFactory(QuizDataStore(context)) ) // 收集Flow为State,自动触发UI重组 val currentLevel = quizViewModel.currentLevel.collectAsState(initial = 1) Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = "当前关卡:${currentLevel.value}") // 模拟关卡切换,完成后保存下一关 Button(onClick = { quizViewModel.saveLevel(currentLevel.value + 1) }) { Text(text = "进入下一关") } } }
三、常见错误排查
- Datastore实例重复创建:必须用
preferencesDataStore扩展属性创建,不要手动new DataStore实例,否则会抛出异常。 - 协程调用错误:
saveCurrentLevel是suspend函数,不能直接在Compose的点击回调中调用,必须通过viewModelScope或lifecycleScope启动协程。 - 未正确监听Flow:在Compose中必须用
collectAsState收集Flow,否则无法获取最新存储值,也不会触发UI更新。 - 默认值缺失:获取关卡时一定要设置默认值(如1),否则首次启动会得到null,导致UI报错。
- Context错误:传递给QuizDataStore的Context必须是有效上下文(如LocalContext.current),不能使用临时Context。
内容的提问来源于stack exchange,提问作者Ayman Ait
相关产品推荐
相关产品推荐

