ViewModel中使用DataStore无法正常保存与读取数据问题
问题原因分析
你的代码存在三个核心问题,导致Pin值始终显示为"tutorial":
- DataStore扩展属性声明错误:
Context.dataStore扩展属性被定义在BasicData类内部,这不符合DataStore的单例设计要求,会导致多个DataStore实例创建,进而使保存的数据无法被正确读取。 - 导航逻辑执行时机过早:
LaunchActivity的页面跳转逻辑在onCreate中立即执行,此时ViewModel还未完成从DataStore加载Pin值的异步操作,导致初始判断始终基于pin=null。 - 冗余的
returnPin方法:该方法完全可以直接使用LiveData的value属性替代,且存在潜在的空指针风险。
解决方案
1. 修复DataStore扩展属性声明
将Context.dataStore扩展属性移至BasicData类外部(文件顶级作用域),确保DataStore单例化:
// BasicData.kt 文件顶级作用域 val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "LaunchSettings") class BasicData(private val context: Context) { private val dataStore = context.dataStore suspend fun set(key: String, given: String) { val dataStoreKey = stringPreferencesKey(key) dataStore.edit { basics -> basics[dataStoreKey] = given } } suspend fun retrieve(key: String): String? { val dataStoreKey = stringPreferencesKey(key) return dataStore.data.first()[dataStoreKey] } }
2. 调整LaunchActivity的导航逻辑
等待ViewModel加载完成数据后再执行页面跳转,使用repeatOnLifecycle确保符合Activity生命周期:
class LaunchActivity : AppCompatActivity() { private val dataModel by viewModels<LaunchVModel>() private var pin: String? = null override fun onCreate(savedInstanceState: Bundle?) { installSplashScreen().apply { setKeepOnScreenCondition { !dataModel.isReady.value } setOnExitAnimationListener { // 保留原有代码 } } super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 监听Pin值更新 dataModel.pin.observe(this) { newPin -> pin = newPin Toast.makeText(this, "Pin val in View Model...$pin", Toast.LENGTH_SHORT).show() } // 等待数据加载完成后执行导航 lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { dataModel.isReady.collect { isReady -> if (isReady) { handleNavigation() } } } } } private fun handleNavigation() { when (pin) { null -> replaceFragment(TutorialFragment()) "tutorial" -> switchActivity() else -> replaceFragment(PasscodeFragment()) } } }
3. 简化ViewModel代码
移除冗余方法,优化初始化逻辑,并确保首次启动时默认值被持久化:
class LaunchVModel(application: Application) : AndroidViewModel(application) { private val _isReady = MutableStateFlow(false) val isReady = _isReady.asStateFlow() private var dataStore = BasicData(application) val pin = MutableLiveData<String>() val email = MutableLiveData<String>() val skip = MutableLiveData<String>() init { viewModelScope.launch { val temp = dataStore.retrieve("pin") if (temp == null) { // 首次启动时将默认值持久化到DataStore dataStore.set("pin", "tutorial") pin.value = "tutorial" } else { pin.value = temp } _isReady.value = true } } // 重命名方法使其语义更清晰 fun savePin(item: String) { viewModelScope.launch { dataStore.set("pin", item) pin.value = item } } }
4. 补充教程页面逻辑
当用户选择跳过Pin设置时,调用dataModel.savePin("tutorial")确保状态被持久化,避免下次启动重复进入教程。
内容的提问来源于stack exchange,提问作者Kafka
相关产品推荐
相关产品推荐

