← Назад к вопросам

Почему надо сохранять состояние в Presenter?

2.0 Middle🔥 121 комментариев
#Архитектура и паттерны#Жизненный цикл и навигация

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сохранение состояния в Presenter: необходимость и реализация

В контексте архитектурных подходов, таких как MVP (Model-View-Presenter), сохранение состояния в Presenter является критически важным для обеспечения стабильности, отказоустойчивости и корректного пользовательского опыта в Android-приложениях. Основная причина заключается в том, что Activity и Fragment подвержены уничтожению и пересозданию системой Android при изменениях конфигурации (например, поворот экрана) или при нехватке памяти.

Почему состояние должно сохраняться именно в Presenter?

  1. Отделение бизнес-логики от жизненного цикла UI: Presenter выступает посредником между View (Activity/Fragment) и Model (данные и бизнес-логика). Если состояние (например, загруженные данные, прогресс операции, флаги) хранится только во View, оно теряется при пересоздании. Это приводит к:

    • Потере данных, загруженных из сети или базы данных.
    • Сбросу состояния UI (например, прогресс-бара, введённого текста).
    • Необходимости повторной загрузки или вычислений, что ухудшает пользовательский опыт и увеличивает потребление ресурсов.
  2. Предотвращение утечек памяти и корректное управление подписками: В Presenter часто управляются асинхронные операции (например, через RxJava или Coroutines). Если не сохранить состояние, при пересоздании View может возникнуть:

    • Утечка старой View (если Presenter сохраняет ссылку на неё).
    • Некорректные обновления UI (например, попытка обновить уничтоженную Activity).
  3. Соблюдение принципа единственной ответственности: Presenter отвечает за обработку действий пользователя и обновление View. Сохранение состояния — часть этой ответственности, так как позволяет восстановить UI после событий жизненного цикла.

Как реализовать сохранение состояния в Presenter?

Пример с использованием AndroidX SavedStateHandle (рекомендуемый подход в связке с ViewModel, который часто выполняет роль Presenter в MVVM, но применимо и к MVP):

// Presenter, интегрированный с ViewModel и SavedStateHandle
class MyPresenter(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    companion object {
        private const val KEY_USER_INPUT = "user_input"
        private const val KEY_LOADING_STATE = "loading_state"
    }

    // Состояние, сохраняемое автоматически через SavedStateHandle
    val userInput: LiveData<String> = savedStateHandle.getLiveData(KEY_USER_INPUT, "")
    val isLoading: LiveData<Boolean> = savedStateHandle.getLiveData(KEY_LOADING_STATE, false)

    fun onUserInputChanged(text: String) {
        // Сохраняем состояние в SavedStateHandle
        savedStateHandle[KEY_USER_INPUT] = text
    }

    fun loadData() {
        savedStateHandle[KEY_LOADING_STATE] = true
        // Асинхронная операция
        viewModelScope.launch {
            val data = repository.fetchData()
            savedStateHandle[KEY_LOADING_STATE] = false
            // Обновляем LiveData для UI
            _dataLiveData.value = data
        }
    }
}

Альтернативный подход в классическом MVP (без ViewModel):

interface MvpView {
    fun showData(data: String)
    fun showProgress(show: Boolean)
}

class MyPresenter : Presenter<MvpView> {
    private var view: MvpView? = null
    private var cachedData: String? = null // Состояние, сохранённое в Presenter
    private var isLoading: Boolean = false

    override fun attachView(view: MvpView) {
        this.view = view
        // Восстанавливаем UI из сохранённого состояния
        cachedData?.let { view.showData(it) }
        if (isLoading) view.showProgress(true)
    }

    override fun detachView() {
        this.view = null // Предотвращаем утечки
    }

    fun loadData() {
        isLoading = true
        view?.showProgress(true)
        // Имитация загрузки
        cachedData = "Загруженные данные"
        isLoading = false
        view?.showProgress(false)
        view?.showData(cachedData!!)
    }

    fun saveState(bundle: Bundle) {
        bundle.putString("cached_data", cachedData)
        bundle.putBoolean("is_loading", isLoading)
    }

    fun restoreState(bundle: Bundle) {
        cachedData = bundle.getString("cached_data")
        isLoading = bundle.getBoolean("is_loading", false)
    }
}

Ключевые выводы:

  • Presenter должен сохранять состояние, чтобы обеспечить целостность данных и непрерывность UX при изменениях конфигурации.
  • Использование SavedStateHandle (в рамках ViewModel) упрощает автоматическое сохранение и восстановление.
  • В классическом MVP требуется ручное управление через методы saveState/restoreState, вызываемые из Activity/Fragment в onSaveInstanceState и onCreate.
  • Это позволяет View оставаться «глупой» (пассивной), делегируя логику состояния Presenter, что соответствует принципам чистой архитектуры.

Таким образом, сохранение состояния в Presenter — это не просто рекомендация, а обязательная практика для создания отказоустойчивых и пользовательски дружественных Android-приложений.

Почему надо сохранять состояние в Presenter? | PrepBro