You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ViewModel中使用DataStore无法正常保存与读取数据问题

问题原因分析

你的代码存在三个核心问题,导致Pin值始终显示为"tutorial":

  1. DataStore扩展属性声明错误:Context.dataStore扩展属性被定义在BasicData类内部,这不符合DataStore的单例设计要求,会导致多个DataStore实例创建,进而使保存的数据无法被正确读取。
  2. 导航逻辑执行时机过早:LaunchActivity的页面跳转逻辑在onCreate中立即执行,此时ViewModel还未完成从DataStore加载Pin值的异步操作,导致初始判断始终基于pin=null。
  3. 冗余的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 06:00:27