【问题标题】:How to restore Model state in MVP?如何在 MVP 中恢复模型状态?
【发布时间】:2019-03-07 01:00:03
【问题描述】:

应用说明

我第一次尝试使用 MVP 实现 Android 应用程序,其中显示一条消息(取自消息池),并在用户单击屏幕时更改。显示所有消息后,该过程将重新开始(遵循相同的消息顺序)。如果应用程序关闭/重新打开,要求是显示相同的消息。所以,我们必须在 MVP 模型中实现一些存储/恢复状态机制。

这是应用程序的基本演示:

MVP 设计

我已经为这个应用实现了这个 MVP,如下所示:

  1. 模型负责什么下一条消息(它实现应用程序状态)。
  2. Presenter 决定何时 请求下一条消息(更新状态),这取决于从用户(通过视图)接收到的事件。李>
  3. 视图决定如何显示实际消息并将用户的事件(点击屏幕)传达给演示者。此外,由于 View 也是 MainActivity,因此需要实例化 Presenter 和 Model 实现。最后,它使用onSaveInstanceState保存模型状态(作为Parcelable)(并恢复它)。

一些代码

(部分)查看实施:

class MainActivity : AppCompatActivity(), ViewMVC {

    private lateinit var presenter: Presenter
    private var model: Model? = CircularModel(LinkedList<State>(Arrays.asList(
            State("First"),
            State("Second"),
            State("Third")

    )))

    override fun onCreate(savedInstanceState: Bundle?) {
        if (savedInstanceState != null) {
            model = savedInstanceState.getParcelable("model")
        }

        presenter = PresenterImpl(this, model!!)
    }

    override fun onSaveInstanceState(outState: Bundle?) {
        outState?.putParcelable("model", model!!)
        super.onSaveInstanceState(outState)
    }

(部分)模型实现:

@Parcelize
class CircularModel constructor(var states: @RawValue Deque<State>?) : Model, Parcelable {

    override fun getModelState(): State {
        return states!!.peekFirst()
    }

    override fun getModelNextState(): State {
        // Black magic happening here!
        return getModelState()
    }
}

问题/我的问题

由于 Presenter 和 Model 应该是“与 Android 无关的”,因此保存应用程序状态(即 Model 对象)由 View 负责。但是,这打破了 View 不知道 Model 的原则。 我的问题是:如何保存模型对象,而视图不知道它的实际实现?在这种情况下处理模型状态的最佳方法是什么?

实际的解决方案可能是编写代码来序列化模型本身中的模型并为每个getNextState() 保存它,但这意味着在模型中使用 Android 调用(并降低其可测试性)。

【问题讨论】:

    标签: android design-patterns model mvp model-view


    【解决方案1】:

    您应该使用不同的持久性机制。 onSaveInstanceState() 真正用于操作系统由于配置/方向更改等原因需要恢复 UI 状态的情况。它不是通用的存储机制。

    模型是保存数据的正确位置,您应该尽量保持模型与 Android 无关,这是正确的。您可以做的是定义一个代表您的持久性要求的接口:

    interface SampleRepo{ 
       fun saveData(...)
       fun getData(...)
    }
    

    然后,您首选的持久性机制(例如 SharedPreferences、SQlite 等)在一个类中实现该接口。这是隐藏您的 Android 特定内容的地方。

    class SharedPrefRepo : SampleRepo{
       override fun saveData(...)
       override fun getData(...)
    }
    

    理想情况下,您需要一些注入机制,这样您就可以将上述实例注入您的模型类(例如 Dagger)。它需要更多的管道代码,但这是松耦合的代价。对于一个更简单的应用程序,比如你正在做的事情,所有这些都是多余的。但是,如果您正在尝试研究正确的 Android 应用架构和松耦合,那么值得探索如何正确进行。

    【讨论】:

    • 感谢 Eugene 的回答,我觉得这是一个很好的解决方案。我还有一些问题: 1. 对SampleRepo 的每次调用都将在CircularModel 模型(模型实现)中执行,对吗? 2. 什么时候拨打saveData?在每个getModelNextState()?还是仅当我们退出应用程序时?
    • 1) - 是的,正确的。 Repo 类将在 Model 类中。 2) - 在这种特殊情况下,我可能只会在应用退出或进入后台时保存它。但是现实世界的实现可能希望使此类数据不断保持同步,因此重要的状态更改一旦发生就会被持久化。例如,如果您的应用在执行其他操作时崩溃,并且您仅在应用进入后台时保存,则应用可能会因数据过时而恢复。
    猜你喜欢
    • 1970-01-01
    • 2020-06-17
    • 2023-03-25
    • 2017-03-05
    • 1970-01-01
    • 1970-01-01
    • 2022-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多