【问题标题】:savedStateHandle not saving StatesavedStateHandle 不保存状态
【发布时间】:2023-02-15 13:51:38
【问题描述】:

this 问题之后,我在我的应用程序中做了一些简单的更改,但它没有像我预期的那样工作。

我有一个计时器,它在计时器完成时发送通知。单击此通知会重新启动 Activity,删除所有计时器信息,主要存储在viewModel。出于这个原因,我决定为viewModel使用保存的状态。

这是我的viewModel

class TimerViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {

private val _secondsRemaining = savedStateHandle.getLiveData<Long>(SECONDS_REMAINING)
val secondsRemaining : LiveData<Long>
    get() = _secondsRemaining

这是我在Fragment中使用viewModel的方法:

private val timerViewModel by viewModels<TimerViewModel>()

当我启动计时器时,我会在时钟的每个滴答声中将剩余秒数保存在 LiveData 中。当计时器结束时,应用程序发送通知,计时器再次启动,计算一个新的周期:

timer = object : CountDownTimer(timerLengthSeconds * 1000, 1000){
        override fun onFinish(){
            (....)
        }

        override fun onTick(millisUntilFinished: Long) {
            var secondsRemainingInCountdown = millisUntilFinished / 1000

            (...)

            _secondsRemaining.value = secondsRemainingInCountdown
         
        }
    }.start()
}

因此,当计时器结束时,应用程序会发送通知,但计时器已重新启动,并且剩余的秒数正在更新(我已通过日志检查过)。 当用户单击通知时,activity 将被终止并重新启动,并且将看到计时器以及保存在 LiveData 中的剩余秒数。但是当activity重启时,LiveData值为空。

我也尝试过将值设置为 10,以防 LiveData 在首次创建时为 null

private val _secondsRemaining = savedStateHandle.getLiveData<Long>(SECONDS_REMAINING, 10)

但是当活动重新启动时,我得到 10 作为 LiveData 的值

我想不通原因。

我的第二个问题是我想保存一个 LiveData 的状态,它存储一个自定义的 class,它保存时钟的状态

private val _timerState = MutableLiveData<TimerState>()
val timerState : LiveData<TimerState>
    get() = _timerState

作为这个class

    sealed class TimerState {

    object OnFocusRunning : TimerState()
    object OnRestRunning : TimerState()
    object OnFocusPaused : TimerState()
    object OnRestPaused : TimerState()
    object Completed : TimerState()
    object RestCompleted : TimerState()
    object NotStarted : TimerState()
}

但我没有成功,因为 TimerState 是自定义的 class 而不是原始类型。

【问题讨论】:

    标签: android kotlin persistence viewmodel viewmodel-savedstate


    【解决方案1】:

    当您使用 SavedStateHandle you need to set your values on it 来存储它们时。如果您对特定键使用 getLiveData,那么只要您为该键设置新值,LiveData 就会更新。如果您直接在 LiveData 上设置它,您将绕过保存的状态:

    class TimerViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    
        // create a LiveData that monitors this key in the saved state
        private val _secondsRemaining = savedStateHandle.getLiveData<Long>(SECONDS_REMAINING)
        val secondsRemaining : LiveData<Long> get() = _secondsRemaining
    
        // expose a setter that updates the state - this will propagate to the LiveData
        fun setSecondsRemaining(remaining: Long) {
            savedStateHandle[SECONDS_REMAINING] = remaining
        }
    
    }
    

    至于您的其他问题,是的,您可以存储的内容有限,对于自定义类,您需要serialise them into a form you can store,或者将它们设为SerializableParcelable

    但是在你的情况下,因为那个密封类除了是一个类型的实例之外没有做任何特殊的事情,所以我只是让它成为一个枚举类 - 这些是 Serializable 所以你可以直接把值扔进去!

    enum class TimerState {
        OnFocusRunning, OnRestRunning // etc
    }
    

    【讨论】:

    • 非常感谢您的回答。我已经尝试过这段代码,但它仍然无法正常工作。当activity 重新启动时,LiveData 仍然为空。也许你可以看看? github.com/arieldipietro/PomodoroTechnique
    • @ArielDiPietro TimerViewModel 在哪里?
    • 抱歉,我在推动项目时可能犯了一个错误。现在应该工作
    • @ArielDiPietro 如果你的意思是你正在登录 TimerViewModelinit 块的两个 LiveDatas,那些对我来说看起来没问题并且没有理由它们应该为空(尤其是你给出默认值的那个0)。但是你知道当应用程序被有意关闭时这种状态会丢失,对吧? SavedStateHandles 仅在系统在后台终止您的应用程序时保留您的数据,因此您可以恢复该状态(如 savedInstanceState 但用于视图模型)。如果你自己终止活动,你会失去那个状态 - 如果你想坚持下去,请使用SharedPreferences或数据库
    • 对不起我的延迟。我想我从一开始就误解了整个概念。我很容易地将所有信息移动到数据库中,这一切都解决了!多谢!
    【解决方案2】:

    SavedStateHandle 几乎完全没有意义。它仅适用于一种特定情况 - 当系统在后台终止我的应用程序时。但还有其他同样(如果不是更多)常见的场景——例如,当用户手动终止应用程序、清除所有应用程序堆栈或重启设备时。在所有这些情况下,SavedStateHandle 都不起作用!

    恕我直言,最好只保留您想自己保留的数据,只需使用SharedPreferencesDataStoreRoom。并且它保证在每个场景中都能正常工作。

    我看不出有什么理由去打扰SavedStateHandle

    【讨论】:

      猜你喜欢
      • 2023-03-29
      • 2018-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-29
      • 2014-02-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多