Как сохранить состояние активности Android с помощью сохранения состояния экземпляра? - PullRequest
2425 голосов
/ 30 сентября 2008

Я работаю на платформе Android SDK, и немного неясно, как сохранить состояние приложения. Итак, учитывая этот незначительный повторный инструментарий примера «Hello, Android»:

package com.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {

  private TextView mTextView = null;

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    if (savedInstanceState == null) {
       mTextView.setText("Welcome to HelloAndroid!");
    } else {
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}

Я думал, что этого будет достаточно для самого простого случая, но он всегда отвечает первым сообщением, независимо от того, как я ухожу из приложения.

Я уверен, что решение так же просто, как переопределение onPause или что-то в этом роде, но я копался в документации около 30 минут и не нашел ничего очевидного.

Ответы [ 28 ]

8 голосов
/ 12 февраля 2018

код Котлина:

сохранить:

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState.apply {
        putInt("intKey", 1)
        putString("stringKey", "String Value")
        putParcelable("parcelableKey", parcelableObject)
    })
}

, а затем в onCreate() или onRestoreInstanceState()

    val restoredInt = savedInstanceState?.getInt("intKey") ?: 1 //default int
    val restoredString = savedInstanceState?.getString("stringKey") ?: "default string"
    val restoredParcelable = savedInstanceState?.getParcelable<ParcelableClass>("parcelableKey") ?: ParcelableClass() //default parcelable

Добавить значения по умолчанию, если вы не хотите иметь Optional

6 голосов
/ 28 сентября 2016

Чтобы получить данные о состоянии активности, хранящиеся в onCreate(), сначала нужно сохранить данные в saveInstanceState, переопределив метод SaveInstanceState(Bundle savedInstanceState).

Когда уничтожается действие, вызывается метод SaveInstanceState(Bundle savedInstanceState), и вы сохраняете данные, которые хотите сохранить. И вы получите то же самое в onCreate() при перезапуске активности. (SaveInstanceState не будет нулевым, поскольку вы сохранили в нем некоторые данные до того, как активность будет уничтожена)

5 голосов
/ 09 августа 2016

Не уверен, что мое решение осуждается или нет, но я использую связанную службу для сохранения состояния ViewModel. Храните ли вы его в памяти в службе или сохраняете и извлекаете из базы данных SQLite, зависит от ваших требований. Это то, что делают сервисы любого типа, они предоставляют такие сервисы, как поддержание состояния приложения и абстрактную общую бизнес-логику.

Из-за ограничений памяти и обработки, присущих мобильным устройствам, я отношусь к представлениям Android аналогично веб-странице. Страница не поддерживает состояние, это просто компонент уровня представления, единственной целью которого является представление состояния приложения и принятие пользовательского ввода. В последних тенденциях в архитектуре веб-приложений используется устаревший шаблон Модель, представление, контроллер (MVC), где страница представляет собой представление, данные домена являются моделью, а контроллер располагается за веб-службой. Тот же самый шаблон может использоваться в Android, когда View, ну ... View, модель - это данные вашего домена, а Controller реализован как служба, привязанная к Android. Всякий раз, когда вы хотите, чтобы представление взаимодействовало с контроллером, связывайте его при запуске / возобновлении и снимайте привязку при останове / паузе.

Этот подход дает вам дополнительный бонус в реализации принципа разделения интересов, заключающегося в том, что бизнес-логика всех ваших приложений может быть перенесена в ваш сервис, что сокращает дублирование логики в нескольких представлениях и позволяет представлению применять другой важный принцип проектирования Единственная ответственность.

5 голосов
/ 08 августа 2016

Простой способ быстро решить эту проблему - IcePick

Сначала настройте библиотеку в app/build.gradle

repositories {
  maven {url "https://clojars.org/repo/"}
}
dependencies {
  compile 'frankiesardo:icepick:3.2.0'
  provided 'frankiesardo:icepick-processor:3.2.0'
}

Теперь давайте проверим этот пример ниже, как сохранить состояние в Activity

public class ExampleActivity extends Activity {
  @State String username; // This will be automatically saved and restored

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Icepick.restoreInstanceState(this, savedInstanceState);
  }

  @Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Icepick.saveInstanceState(this, outState);
  }
}

Он работает для действий, фрагментов или любых объектов, которым необходимо сериализовать свое состояние в пакете (например, ViewPresenters для миномета)

Icepick также может генерировать код состояния экземпляра для пользовательских видов:

class CustomView extends View {
  @State int selectedPosition; // This will be automatically saved and restored

  @Override public Parcelable onSaveInstanceState() {
    return Icepick.saveInstanceState(this, super.onSaveInstanceState());
  }

  @Override public void onRestoreInstanceState(Parcelable state) {
    super.onRestoreInstanceState(Icepick.restoreInstanceState(this, state));
  }

  // You can put the calls to Icepick into a BaseCustomView and inherit from it
  // All Views extending this CustomView automatically have state saved/restored
}
2 голосов
/ 27 августа 2018

Теперь Android предоставляет ViewModels для сохранения состояния, вы должны попытаться использовать это вместо saveInstanceState.

0 голосов
/ 01 июня 2019

Котлин

Вы должны переопределить onSaveInstanceState и onRestoreInstanceState, чтобы сохранить и извлечь переменные, которые вы хотите сохранить

График жизненного цикла

image

Хранить переменные

public override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)

    // prepare variables here
    savedInstanceState.putInt("kInt", 10)
    savedInstanceState.putBoolean("kBool", true)
    savedInstanceState.putDouble("kDouble", 4.5)
    savedInstanceState.putString("kString", "Hello Kotlin")
}

Получить переменные

public override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)

    val myInt = savedInstanceState.getInt("kInt")
    val myBoolean = savedInstanceState.getBoolean("kBool")
    val myDouble = savedInstanceState.getDouble("kDouble")
    val myString = savedInstanceState.getString("kString")
    // use variables here
}
0 голосов
/ 27 февраля 2019

У меня есть идея получше. Было бы лучше сохранить ваши данные без повторного вызова onCreate. Вы можете отключить его от активности, когда ориентация меняется.

В вашем манифесте:

<activity android:name=".MainActivity"
        android:configChanges="orientation|screenSize">

Edit: Это решение не будет работать, когда Android убивает процесс из-за нехватки памяти. если вам нужно хранить данные наверняка, вам нужно использовать SavedInstanceState в этом случае, в противном случае я предлагаю использовать этот путь классный и простой в использовании!

0 голосов
/ 10 января 2019

Что сохранить, а что нет?

Когда-нибудь задумывались, почему текст в EditText автоматически сохраняется при изменении ориентации? Ну, этот ответ для вас.

Когда экземпляр Действия уничтожается, и Система заново создает новый экземпляр (например, изменение конфигурации). Он пытается воссоздать его, используя набор сохраненных данных старого состояния активности ( состояние экземпляра ).

Состояние экземпляра - это набор пар ключ-значение , хранящихся в объекте Bundle.

По умолчанию система сохраняет объекты View, например, в Bundle.

  • Текст в EditText
  • Положение прокрутки в ListView и т. Д.

Если вам нужно сохранить другую переменную как часть состояния экземпляра, вам следует использовать метод OVERRIDE onSavedInstanceState(Bundle savedinstaneState).

Например, int currentScore в GameActivity

Подробнее о onSavedInstanceState (Bundle saveinstaneState) при сохранении данных

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}

Так что по ошибке, если вы забудете позвонить super.onSaveInstanceState(savedInstanceState); поведение по умолчанию не будет работать, т.е. текст в EditText не будет сохранен.

Что выбрать для восстановления состояния активности?

 onCreate(Bundle savedInstanceState)

OR

onRestoreInstanceState(Bundle savedInstanceState)

Оба метода получают один и тот же объект Bundle, поэтому не имеет значения, где вы пишете логику восстановления. Единственное отличие состоит в том, что в методе onCreate(Bundle savedInstanceState) вы должны будете дать нулевую проверку, пока она не нужна в последнем случае. Другие ответы уже содержат фрагменты кода. Вы можете отослать их.

Подробнее о onRestoreInstanceState (Bundle saveinstaneState)

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from the saved instance
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
}

Всегда вызывайте super.onRestoreInstanceState(savedInstanceState);, чтобы система по умолчанию восстановила иерархию просмотра

Бонус

onSaveInstanceState(Bundle savedInstanceState) вызывается системой только тогда, когда пользователь намеревается вернуться в действие. Например, вы используете приложение X и неожиданно получаете звонок. Вы переходите в приложение вызывающего абонента и возвращаетесь в приложение X. В этом случае будет вызван метод onSaveInstanceState(Bundle savedInstanceState).

Но учтите это, если пользователь нажимает кнопку возврата. Предполагается, что пользователь не намерен возвращаться в действие, поэтому в этом случае система не будет вызывать onSaveInstanceState(Bundle savedInstanceState). Вы должны учитывать все сценарии при сохранении данных.

Соответствующие ссылки:

Демонстрация поведения по умолчанию
Официальная документация Android .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...