如何在 MVP 中恢复模型状态?
How to restore Model state in MVP?
应用说明
我第一次尝试使用 MVP 实现 Android 应用程序,其中显示一条消息(从消息池中获取)并在用户单击屏幕时更改。一旦显示了所有消息,该过程将重新开始(按照相同的消息顺序)。如果应用程序是 closed/reopened,则要求显示相同的消息。因此,我们必须在 MVP 模型中实现一些 store/restore 状态机制。
这是应用程序的基本演示:
MVP 设计
我已经为此应用实现了这个 MVP,如下所示:
- 模型负责什么它将成为下一条消息(它实现了应用程序状态)。
- Presenter 决定何时 请求下一条消息(更新状态),这取决于从用户接收到的事件(通过视图)。
- 视图 决定如何 显示实际消息并将用户(点击屏幕)的事件传达给演示者。此外,由于 View 也是
MainActivity
,它负责 实例化 Presenter 和 Model 的实现。最后,它 保存 模型状态(作为 Parcelable
)onSaveInstanceState
(并恢复它)。
一些代码
(部分)查看实现:
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 的原则。 我的问题是:如何在 View 不知道其实际实现的情况下保存 Model 对象?在这种情况下处理模型状态的最佳方法是什么?
一个实际的解决方案可能是编写代码以在模型本身中序列化模型并为每个 getNextState()
保存它,但这意味着在模型中使用 Android 调用(并减少它的可测试性)。
您应该使用不同的持久性机制。 onSaveInstanceState() 真正用于 OS 由于配置/方向更改等原因需要恢复 UI 状态的情况。它不是通用存储机制。
模型是保存数据的正确位置,您应该尽量使模型保持 Android 不可知是正确的。您可以做的是定义一个表示您的持久性要求的接口:
interface SampleRepo{
fun saveData(...)
fun getData(...)
}
然后您首选的持久性机制(例如 SharedPreferences、SQlite 等)在 class 中实现该接口。这是您的 Android 特定内容将被隐藏的地方。
class SharedPrefRepo : SampleRepo{
override fun saveData(...)
override fun getData(...)
}
理想情况下,您需要一些注入机制,这样您就可以将上述实例注入到您的模型中 class(例如 Dagger)。它需要更多的管道代码,但这是松散耦合的代价。对于一个更简单的应用程序,就像您正在做的那样,所有这些都太过分了。但是,如果您正在尝试研究适当的 Android 应用程序架构和松散耦合,那么值得探索如何正确地做到这一点。
应用说明
我第一次尝试使用 MVP 实现 Android 应用程序,其中显示一条消息(从消息池中获取)并在用户单击屏幕时更改。一旦显示了所有消息,该过程将重新开始(按照相同的消息顺序)。如果应用程序是 closed/reopened,则要求显示相同的消息。因此,我们必须在 MVP 模型中实现一些 store/restore 状态机制。
这是应用程序的基本演示:
MVP 设计
我已经为此应用实现了这个 MVP,如下所示:
- 模型负责什么它将成为下一条消息(它实现了应用程序状态)。
- Presenter 决定何时 请求下一条消息(更新状态),这取决于从用户接收到的事件(通过视图)。
- 视图 决定如何 显示实际消息并将用户(点击屏幕)的事件传达给演示者。此外,由于 View 也是
MainActivity
,它负责 实例化 Presenter 和 Model 的实现。最后,它 保存 模型状态(作为Parcelable
)onSaveInstanceState
(并恢复它)。
一些代码
(部分)查看实现:
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 的原则。 我的问题是:如何在 View 不知道其实际实现的情况下保存 Model 对象?在这种情况下处理模型状态的最佳方法是什么?
一个实际的解决方案可能是编写代码以在模型本身中序列化模型并为每个 getNextState()
保存它,但这意味着在模型中使用 Android 调用(并减少它的可测试性)。
您应该使用不同的持久性机制。 onSaveInstanceState() 真正用于 OS 由于配置/方向更改等原因需要恢复 UI 状态的情况。它不是通用存储机制。
模型是保存数据的正确位置,您应该尽量使模型保持 Android 不可知是正确的。您可以做的是定义一个表示您的持久性要求的接口:
interface SampleRepo{
fun saveData(...)
fun getData(...)
}
然后您首选的持久性机制(例如 SharedPreferences、SQlite 等)在 class 中实现该接口。这是您的 Android 特定内容将被隐藏的地方。
class SharedPrefRepo : SampleRepo{
override fun saveData(...)
override fun getData(...)
}
理想情况下,您需要一些注入机制,这样您就可以将上述实例注入到您的模型中 class(例如 Dagger)。它需要更多的管道代码,但这是松散耦合的代价。对于一个更简单的应用程序,就像您正在做的那样,所有这些都太过分了。但是,如果您正在尝试研究适当的 Android 应用程序架构和松散耦合,那么值得探索如何正确地做到这一点。