Я пытаюсь впервые реализовать приложение для Android с MVP, где отображается сообщение (взятое из пула сообщений), и оно изменяется, когда пользователь нажимает на экран. Как только все сообщения будут отображены, процесс начнется заново (следуя тому же порядку сообщений). Требование состоит в том, чтобы отображать одно и то же сообщение, если приложение закрыто/повторно открыто. Итак, мы должны реализовать некоторый механизм сохранения/восстановления состояния в модели MVP.
Вот базовая демонстрация приложения:
Я реализовал этот MVP для этого приложения следующим образом:
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). Однако это нарушает принцип, согласно которому представление не знает модели. Мой вопрос: как сохранить объект модели, если представление не знает о его фактической реализации? Каков наилучший способ справиться с состоянием модели в этом сценарии?
Фактическим решением может быть написание кода для сериализации модели в самой модели и сохранения его для каждого getNextState(), но это будет означать использование вызовов Android в модели (и снижение ее тестируемости).
Вы должны использовать другой механизм сохраняемости. OnSaveInstanceState() действительно используется в ситуациях, когда ОС необходимо восстановить состояние пользовательского интерфейса из-за таких вещей, как изменения конфигурации/ориентации. Это не универсальный механизм хранения.
Модель — это правильное место для хранения данных, и правильно, что вы должны стараться, чтобы модель была как можно более независимой от Android. Что вы можете сделать, так это определить интерфейс, который представляет ваши требования к сохраняемости:
interface SampleRepo{
fun saveData(...)
fun getData(...)
}
Затем ваш предпочтительный механизм сохранения (например, SharedPreferences, SQlite и т. д.) в классе реализует этот интерфейс. Здесь будут скрыты ваши специфические вещи для Android.
class SharedPrefRepo : SampleRepo{
override fun saveData(...)
override fun getData(...)
}
В идеале вам понадобится некоторый механизм внедрения, чтобы вы могли внедрить экземпляр вышеперечисленного в класс вашей модели (например, Dagger). Для этого требуется дополнительный код сантехники, но это цена слабой связи. Для более простого приложения, такого как то, что вы делаете, все это излишне. Но если вы пытаетесь изучить правильную архитектуру приложений для Android и слабую связанность, стоит изучить, как это сделать правильно.
1) - да, верно. Класс Repo будет внутри класса Model. 2) - В этом конкретном случае я бы, вероятно, сохранил его только тогда, когда приложение выходит или переходит в фоновый режим. Но в реальной реализации может потребоваться постоянная синхронизация таких данных, чтобы важные изменения состояния сохранялись, как только они происходят. Если ваше приложение разбилось, например, когда оно делало что-то еще, и вы сохраняете только тогда, когда приложение переходит в фоновый режим, тогда приложение может быть восстановлено с устаревшими данными.
Спасибо, Евгений, за ваш ответ, я нахожу это очень хорошим решением. У меня все еще есть несколько вопросов: 1. Каждый вызов
SampleRepoвызова будет выполняться вCircularModelмодели (реализация модели), верно? 2. Когда звонитьsaveData? На каждомgetModelNextState()? Или только при выходе из приложения?