Сохранение состояния активности Android с помощью сохранения состояния экземпляра

Я работал на платформе 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 минут или так и не нашли ничего очевидного.

27 ответов


вам нужно переопределить onSaveInstanceState(Bundle savedInstanceState) и напишите значения состояния приложения, которые вы хотите изменить на


на savedInstanceState предназначен только для сохранения состояния, связанного с текущим экземпляром действия, например текущей навигации или информации выбора, так что если Android уничтожает и воссоздает действие, оно может вернуться, как это было раньше. См. документацию для onCreate и onSaveInstanceState

для более длительного состояния рассмотрите возможность использования базы данных SQLite, файла или настроек. См.Сохранение Постоянного Состояния.


обратите внимание, что он является не безопасно использовать onSaveInstanceState и onRestoreInstanceState для постоянных данных, согласно документации по состояниям деятельности в http://developer.android.com/reference/android/app/Activity.html.

в документе говорится (в разделе "жизненный цикл активности"):

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

другими словами, поместите код сохранения/восстановления для постоянных данных в onPause() и onResume()!

редактировать: для дальнейшего уточнения, вот onSaveInstanceState() документы:

этот метод вызывается до того, как действие может быть убито, так что, когда он приходит назад некоторое время в будущем оно может восстановить свое государство. Для например, если действие B запускается перед действием A, а на некоторых действие точки A убито, чтобы вернуть ресурсы, действие A будет иметь возможность сохранить текущее состояние пользовательского интерфейса с помощью этого метод таким образом, когда пользователь возвращается к действию A, состояние пользовательский интерфейс можно восстановить через onCreate(Bundle) или onRestoreInstanceState(Bundle).


мой коллега написал статью, объясняющую состояние приложения на устройствах Android, включая пояснения по жизненному циклу активности и информации о состоянии, как хранить информацию о состоянии и сохранять в состояние Bundle и SharedPreferences и посмотреть здесь.

статья охватывает три подхода:

храните локальные данные Управления varible / UI для срока службы приложения (т. е. временно) с помощью пакета состояний экземпляра

[Code sample – Store State in State Bundle]
@Override
public void onSaveInstanceState(Bundle savedInstanceState) 
{
  // Store UI state to the savedInstanceState.
  // This bundle will be passed to onCreate on next call.  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  savedInstanceState.putString(“Name”, strName);
  savedInstanceState.putString(“Email”, strEmail);
  savedInstanceState.putBoolean(“TandC”, blnTandC);

  super.onSaveInstanceState(savedInstanceState);
}

магазин местный varible / UI control data между экземплярами приложений (т. е. постоянно) с использованием общих настроек

[Code sample – Store State in SharedPreferences]
@Override
protected void onPause() 
{
  super.onPause();

  // Store values between instances here
  SharedPreferences preferences = getPreferences(MODE_PRIVATE);
  SharedPreferences.Editor editor = preferences.edit();  // Put the values from the UI
  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  editor.putString(“Name”, strName); // value to store
  editor.putString(“Email”, strEmail); // value to store
  editor.putBoolean(“TandC”, blnTandC); // value to store    
  // Commit to storage
  editor.commit();
}

сохранение экземпляров объектов в памяти между действиями в течение срока службы приложения с использованием сохраненного экземпляра без конфигурации

[Code sample – store object instance]
private cMyClassType moInstanceOfAClass;// Store the instance of an object
@Override
public Object onRetainNonConfigurationInstance() 
{
  if (moInstanceOfAClass != null) // Check that the object exists
      return(moInstanceOfAClass);
  return super.onRetainNonConfigurationInstance();
}

Это классический "gotcha" развития Android. Здесь есть два вопроса:

  • существует тонкая ошибка Android Framework, которая значительно усложняет управление стеком приложений во время разработки, по крайней мере, в устаревших версиях (не совсем уверен, если/когда/как это было исправлено). Я рассмотрю эту ошибку ниже.
  • "нормальный" или предполагаемый способ управления этой проблемой сам по себе довольно сложен с двойственностью onPause / onResume и данные метода onsaveinstance/onRestoreInstanceState

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

во-первых, чтобы прояснить "предполагаемое" поведение: onSaveInstance и onRestoreInstance являются хрупкими и только для переходного состояния. Предполагаемое использование (afaict) для обработки отдыха активности, когда телефон поворот (изменение ориентации). Другими словами, предполагаемое использование-это когда ваша деятельность все еще логически "сверху", но все еще должна быть восстановлена системой. Сохраненный пакет не сохраняется за пределами process / memory / gc, поэтому вы не можете полагаться на это, если ваша деятельность идет в фоновом режиме. Да, возможно, память вашей деятельности переживет свое путешествие на задний план и избежит GC, но это ненадежно (и не предсказуемо).

Так что если у вас есть сценарий, где существует значимый "прогресс пользователя" или состояние, которое должно сохраняться между "запусками" вашего приложения, руководство по использованию onPause и onResume. Вы должны сами выбрать и подготовить постоянное хранилище.

но-есть очень запутанная ошибка, которая усложняет все это. Детали здесь:

http://code.google.com/p/android/issues/detail?id=2373

http://code.google.com/p/android/issues/detail?id=5277

в основном, если ваше приложение запускается с флагом SingleTask, а затем позже вы запускаете его с главного экрана или меню запуска, то этот последующий вызов создаст новую задачу ... вы фактически будете иметь два разных экземпляра вашего приложения, населяющих один и тот же стек ... который становится очень странным очень быстро. Это происходит при запуске приложения во время разработки (например, из Eclipse или Intellij), поэтому разработчики часто сталкиваются с этим. Но также через некоторые из механизмов обновления app store (так что это также влияет на ваших пользователей).

Я боролся с этими потоками в течение нескольких часов, прежде чем понял, что моей главной проблемой была эта ошибка, а не предполагаемое поведение фреймворка. Большое сочинение и решение (обновление: см. ниже), похоже, от пользователя @kaciula в этом ответе:

Главная клавиша нажмите поведение

обновление июнь 2013: месяцы спустя я, наконец, нашел "правильное" решение. Вам не нужно самостоятельно управлять флагами startedapp, вы можете обнаружить это из фреймворка и внести соответствующий залог. Я использую это в начале моей LauncherActivity.метод onCreate:

if (!isTaskRoot()) {
    Intent intent = getIntent();
    String action = intent.getAction();
    if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action != null && action.equals(Intent.ACTION_MAIN)) {
        finish();
        return;
    }
}

onSaveInstanceState вызывается, когда система нуждается в памяти и убивает приложение. Это не вызывается, когда пользователь просто закрывает приложение. Поэтому я думаю, что состояние приложения также должно быть сохранено в onPause Он должен быть сохранен в некотором постоянном хранилище, например Preferences или Sqlite


оба метода полезны и действительны, и оба лучше всего подходят для разных сценариев:

  1. пользователь завершает приложение и повторно открывает его позже, но приложение должно перезагрузить данные из последнего сеанса – это требует постоянного подхода к хранению, такого как использование SQLite.
  2. пользователь переключает приложение, а затем возвращается к оригиналу и хочет забрать, где они остановились - сохранить и восстановить данные пакета (например, приложение государственные данные) в onSaveInstanceState() и onRestoreInstanceState() обычно достаточно.

если вы сохраняете данные состояния постоянным образом, его можно перезагрузить в onResume() или onCreate() (или фактически при любом вызове жизненного цикла). Это может быть или не быть желательным поведением. Если вы храните его в пачке в InstanceState, то он является переходным и подходит только для хранения данных для использования в том же пользовательском "сеансе" (я использую термин "сеанс" свободно), но не между "сеансами".

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


сохранение состояния-это kludge в лучшем случае, насколько я обеспокоен. Если вам нужно сохранить постоянные данные, просто использовать SQLite


думаю, я нашел ответ. Позвольте мне рассказать, что я сделал простыми словами:

Предположим, у меня есть два вида деятельности, activity1 и activity2, и я перехожу от activity1 к activity2 (я сделал некоторые работы в activity2) и снова к действию 1, нажав на кнопку в activity1. Теперь на этом этапе я хотел вернуться к activity2, и я хочу видеть свою activity2 в том же состоянии, когда я в последний раз покидал activity2.

для приведенного выше сценария, что я сделал разве что в манифесте я сделал некоторые изменения вроде этого:

<activity android:name=".activity2"
          android:alwaysRetainTaskState="true"      
          android:launchMode="singleInstance">
</activity>

и в activity1 на кнопке нажмите событие, которое я сделал так:

Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.setClassName(this,"com.mainscreen.activity2");
startActivity(intent);

и в activity2 на кнопке нажмите событие, которое я сделал так:

Intent intent=new Intent();
intent.setClassName(this,"com.mainscreen.activity1");
startActivity(intent);

Теперь произойдет то, что все изменения, которые мы внесли в activity2, не будут потеряны, и мы сможем просмотреть activity2 в том же состоянии, в котором мы оставили ранее.

Я считаю, что это ответ, и это работает штраф для меня. Поправьте меня, если я ошибаюсь.


воссоздание активности

есть несколько сценариев, в которых ваша деятельность уничтожается из-за нормального поведения приложения, например, когда пользователь нажимает кнопку "Назад" или ваша деятельность сигнализирует о своем собственном уничтожении, вызывая finish(). Система также может уничтожить вашу деятельность, если она в настоящее время остановлена и не использовалась в течение длительного времени или активность переднего плана требует больше ресурсов, поэтому система должна закрыть фоновые процессы для восстановления память.

когда activity уничтожается, потому что пользователь нажимает назад или activity завершает себя, концепция системы этого Activity экземпляр исчез навсегда, потому что поведение указывает, что активность больше не нужна. Однако, если система уничтожает действие из-за системных ограничений (а не обычного поведения приложения), то, хотя фактический экземпляр действия исчез, система помнит, что он существовал так, что если пользователь переходит к нему, система создает новый экземпляр действия, используя набор сохраненных данных, который описывает состояние действия, когда оно было destroyed. Сохраненные данные, используемые системой для восстановления предыдущего состояния, называются "состоянием экземпляра" и представляют собой коллекцию пар ключ-значение, хранящихся в объекте Bundle.

чтобы сохранить дополнительные данные о состоянии активности, необходимо переопределить метод обратного вызова onSaveInstanceState (). Система вызывает этот метод, когда пользователь покидает вашу активность и передает ему объект Bundle, который будет сохранен в случае, если ваша активность будет неожиданно уничтожена. Если система должна воссоздать экземпляр activity позже, она передает один и тот же объект Bundle обоим onRestoreInstanceState() и onCreate() методы. enter image description here

когда система начинает останавливать вашу деятельность, она вызывает onSaveInstanceState() (1) таким образом, вы можете указать дополнительные данные состояния, которые вы хотите сохранить, если экземпляр действия должен быть воссоздан. Если деятельность уничтожена и один и тот же экземпляр должен быть воссоздан, система передает данные состояния, определенные в (1), обоим onCreate() Метод (2) и onRestoreInstanceState() метод (3).

Сохранить Activity государство

когда ваша деятельность начинает останавливаться, система вызывает onSaveInstanceState() таким образом, ваша деятельность может сохранять информацию о состоянии с коллекцией пар ключ-значение. Реализация этого метода по умолчанию сохраняет информацию о состоянии иерархии представлений действия, например текст в Ан EditText виджет или положение прокрутки ListView.

чтобы сохранить дополнительную информацию о состоянии для вашей деятельности, вы должны реализовать onSaveInstanceState() и добавьте пары ключ-значение в объект Bundle. Например:

  static final String STATE_SCORE = "playerScore";
  static final String STATE_LEVEL = "playerLevel";

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

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

внимание: всегда вызывайте реализацию суперкласса onSaveInstanceState() таким образом, реализация по умолчанию может сохранить состояние иерархии представлений.

Восстановить Activity государство

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

потому что onCreate() метод вызывается независимо от того, создает ли система новый экземпляр вашей активности или воссоздает предыдущий, вы должны проверить, является ли пакет состояния нулевым, прежде чем пытаться его прочитать. Если это null, затем система создает новый экземпляр действия вместо восстановления предыдущего, который был уничтожен.

например, вот как вы можете восстановить некоторые данные состояния в onCreate():

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState); // Always call the superclass first

 // Check whether we're recreating a previously destroyed instance
 if (savedInstanceState != null) {
    // Restore value of members from saved state
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
 } else {
    // Probably initialize members with default values for a new instance
 }

 }

вместо восстановления состояния во время onCreate() вы можете выбрать для реализации onRestoreInstanceState(), который система вызывает после onStart() метод. Система вызывает onRestoreInstanceState() только если есть сохраненное состояние для восстановления, поэтому вам не нужно проверять, является ли пакет значение null:

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

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

onSaveInstanceState() для переходных данных (восстановлено в onCreate()/onRestoreInstanceState()),onPause() для постоянных данных (восстановлен в onResume()). Из технических ресурсов Android:

данные метода onsaveinstance() вызывается Android, если действие прекращается и может быть убито до его возобновления! Это означает, что он должен хранить любое состояние, необходимое для повторной инициализации в том же состоянии при перезапуске действия. Это аналог метода onCreate (), и на самом деле пакет savedInstanceState переданного в метод onCreate() является одной связке, что вы построить как outState в данные метода onsaveinstance() метод.

onPause () и onResume() также являются бесплатными методами. onPause () всегда вызывается, когда действие заканчивается, даже если мы инициировали это (например, с вызовом finish ()). Мы будем использовать это, чтобы сохранить текущую запись в базе данных. Передовая практика заключается в высвобождении любых ресурсов, которые могут быть высвобождены в методов onPause (), чтобы занимать меньше ресурсов, когда в пассивном состоянии.


действительно onSaveInstance состояние callen, когда действие переходит в background

цитата из документации: - метод! .. --1--> вызывается перед помещением действия в такое фоновое состояние"


между тем я вообще больше не использую

Bundle savedInstanceState & Co

живой цикл для большинства видов деятельности слишком сложен и не нужен. И сам google заявляет, что он даже не надежен.

мой способ-сохранить любые изменения сразу в настройках

 SharedPreferences p;
 p.edit().put(..).commit()

в некотором роде SharedPreferences работают аналогично пакетам. И естественно и поначалу такие значения должны быть красными от предпочтений.

в случае сложных данных вы можете использовать SQLite вместо использования предпочтений.

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


чтобы уменьшить шаблон, я использую следующее interface и class читать/писать Bundle для сохранения состояния экземпляра.


сначала создайте интерфейс, который будет использоваться для аннотирования переменных экземпляра:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({
        ElementType.FIELD
})
public @interface SaveInstance {

}

затем создайте класс, в котором отражение будет использоваться для сохранения значений в комплекте:

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;

import java.io.Serializable;
import java.lang.reflect.Field;

/**
 * Save and load fields to/from a {@link Bundle}. All fields should be annotated with {@link
 * SaveInstance}.</p>
 */
public class Icicle {

    private static final String TAG = "Icicle";

    /**
     * Find all fields with the {@link SaveInstance} annotation and add them to the {@link Bundle}.
     *
     * @param outState
     *         The bundle from {@link Activity#onSaveInstanceState(Bundle)} or {@link
     *         Fragment#onSaveInstanceState(Bundle)}
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @see #load(Bundle, Object)
     */
    public static void save(Bundle outState, Object classInstance) {
        save(outState, classInstance, classInstance.getClass());
    }

    /**
     * Find all fields with the {@link SaveInstance} annotation and add them to the {@link Bundle}.
     *
     * @param outState
     *         The bundle from {@link Activity#onSaveInstanceState(Bundle)} or {@link
     *         Fragment#onSaveInstanceState(Bundle)}
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @param baseClass
     *         Base class, used to get all superclasses of the instance.
     * @see #load(Bundle, Object, Class)
     */
    public static void save(Bundle outState, Object classInstance, Class<?> baseClass) {
        if (outState == null) {
            return;
        }
        Class<?> clazz = classInstance.getClass();
        while (baseClass.isAssignableFrom(clazz)) {
            String className = clazz.getName();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(SaveInstance.class)) {
                    field.setAccessible(true);
                    String key = className + "#" + field.getName();
                    try {
                        Object value = field.get(classInstance);
                        if (value instanceof Parcelable) {
                            outState.putParcelable(key, (Parcelable) value);
                        } else if (value instanceof Serializable) {
                            outState.putSerializable(key, (Serializable) value);
                        }
                    } catch (Throwable t) {
                        Log.d(TAG, "The field '" + key + "' was not added to the bundle");
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
    }

    /**
     * Load all saved fields that have the {@link SaveInstance} annotation.
     *
     * @param savedInstanceState
     *         The saved-instance {@link Bundle} from an {@link Activity} or {@link Fragment}.
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @see #save(Bundle, Object)
     */
    public static void load(Bundle savedInstanceState, Object classInstance) {
        load(savedInstanceState, classInstance, classInstance.getClass());
    }

    /**
     * Load all saved fields that have the {@link SaveInstance} annotation.
     *
     * @param savedInstanceState
     *         The saved-instance {@link Bundle} from an {@link Activity} or {@link Fragment}.
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @param baseClass
     *         Base class, used to get all superclasses of the instance.
     * @see #save(Bundle, Object, Class)
     */
    public static void load(Bundle savedInstanceState, Object classInstance, Class<?> baseClass) {
        if (savedInstanceState == null) {
            return;
        }
        Class<?> clazz = classInstance.getClass();
        while (baseClass.isAssignableFrom(clazz)) {
            String className = clazz.getName();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(SaveInstance.class)) {
                    String key = className + "#" + field.getName();
                    field.setAccessible(true);
                    try {
                        Object fieldVal = savedInstanceState.get(key);
                        if (fieldVal != null) {
                            field.set(classInstance, fieldVal);
                        }
                    } catch (Throwable t) {
                        Log.d(TAG, "The field '" + key + "' was not retrieved from the bundle");
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
    }

}

пример использования:

public class MainActivity extends Activity {

    @SaveInstance
    private String foo;

    @SaveInstance
    private int bar;

    @SaveInstance
    private Intent baz;

    @SaveInstance
    private boolean qux;

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

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

}

Примечание: этот код был адаптирован из проект библиотекиAndroidAutowire который лицензирован под лицензия MIT.


чтобы ответить на исходный вопрос напрямую. savedInstancestate имеет значение null, поскольку ваша активность никогда не создается повторно.

ваша деятельность будет воссоздана только с помощью пакета состояний, когда:

  • изменения конфигурации, такие как изменение ориентации или языка телефона, который может потребовать создания нового экземпляра действия.
  • вы возвращаетесь к приложению из фона после того, как ОС уничтожила активность.

Android уничтожит фоновые действия, когда под давлением памяти или после того, как они были в фоновом режиме в течение длительного периода времени.

при тестировании вашего примера hello world есть несколько способов уйти и вернуться к деятельности.

  • когда вы нажимаете кнопку Назад, действие завершено. Повторный запуск приложения-это совершенно новый экземпляр. Ты вообще не возвращаешься к прошлому.
  • при нажатии кнопки "Домой" или использовании переключателя задач активность отойдет на задний план. При переходе обратно в приложение onCreate будет вызываться только в том случае, если действие должно быть уничтожено.

в большинстве случаев, если вы просто нажимаете home, а затем снова запускаете приложение, Активность не нужно будет повторно создавать. Он уже существует в памяти, поэтому onCreate () не будет вызываться.

в разделе Настройки -> Параметры разработчика есть опция "Не сохранять действия". Когда он включен Android всегда будет уничтожьте действия и воссоздайте их, когда они будут восстановлены. Это отличный вариант оставить включенным при разработке, потому что он имитирует наихудший сценарий. (Низкое устройство памяти рециркуляции вашей деятельности все время ).

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


на onSaveInstanceState(bundle) и onRestoreInstanceState(bundle) методы полезны для сохранения данных просто при вращении экрана (изменение ориентации).
Они даже не хороши при переключении между приложениями (так как onSaveInstanceState() метод называется but onCreate(bundle) и onRestoreInstanceState(bundle) больше не вызывается.
Для большей сохраняемости используйте общие настройки. прочитать эту статью


моя проблема заключалась в том, что я нуждался в постоянстве только во время жизни приложения (т. е. одно выполнение, включая запуск других под-действий в том же приложении и поворот устройства и т. д.). Я пробовал различные комбинации вышеуказанных ответов, но не получал того, что хотел во всех ситуациях. В конце концов, мне удалось получить ссылку на savedInstanceState во время onCreate:

mySavedInstanceState=savedInstanceState;

и использовать это, чтобы получить содержимое моей переменной, когда мне это нужно, по строчкам:

if (mySavedInstanceState !=null) {
   boolean myVariable = mySavedInstanceState.getBoolean("MyVariable");
}

я использую onSaveInstanceStateи onRestoreInstanceState как было предложено выше, но я думаю, что я мог бы также или альтернативно использовать мой метод для сохранения переменной при ее изменении (например, используя putBoolean)


хотя принятый ответ правильный, есть более быстрый и простой способ сохранить состояние активности на Android с помощью библиотеки под названием Пешне. Icepick-это процессор аннотаций, который заботится обо всем шаблонном коде, используемом для сохранения и восстановления состояния.

делать что-то подобное с Icepick:

class MainActivity extends Activity {
  @State String username; // These will be automatically saved and restored
  @State String password;
  @State int age;

  @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);
  }
}

это то же самое, что делать это:

class MainActivity extends Activity {
  String username;
  String password;
  int age;

  @Override
  public void onSaveInstanceState(Bundle savedInstanceState) {
    super.onSaveInstanceState(savedInstanceState);
    savedInstanceState.putString("MyString", username);
    savedInstanceState.putString("MyPassword", password);
    savedInstanceState.putInt("MyAge", age); 
    /* remember you would need to actually initialize these variables before putting it in the
    Bundle */
  }

  @Override
  public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    username = savedInstanceState.getString("MyString");
    password = savedInstanceState.getString("MyPassword");
    age = savedInstanceState.getInt("MyAge");
  }
}

ледокол будет работать с любым объектом, который сохраняет свое состояние с Bundle.


есть в основном два способа реализовать это изменение.

  1. используя onSaveInstanceState() и onRestoreInstanceState().
  2. в манифесте android:configChanges="orientation|screenSize".

Я действительно не рекомендую использовать второй метод. Поскольку в одном из моих опытов это вызывало половину экрана устройства черным при вращении от портрета к пейзажу и наоборот.

используя первый метод, упомянутый выше, мы можем сохранять данные при изменении ориентации или изменении конфигурации происходит. Я знаю способ, которым вы можете хранить любые данные внутри объекта состояния savedInstance.

пример: рассмотрим случай, если вы хотите сохранить объект Json. создайте класс модели с геттерами и сеттерами .

class MyModel extends Serializable{
JSONObject obj;

setJsonObject(JsonObject obj)
{
this.obj=obj;
}

JSONObject getJsonObject()
return this.obj;
} 
}

теперь в вашей активности в onCreate и методом данные метода onsaveinstance выполните следующие действия. Это будет выглядеть примерно так:

@override
onCreate(Bundle savedInstaceState){
MyModel data= (MyModel)savedInstaceState.getSerializable("yourkey")
JSONObject obj=data.getJsonObject();
//Here you have retained JSONObject and can use.
}


@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Obj is some json object 
MyModel dataToSave= new MyModel();
dataToSave.setJsonObject(obj);
oustate.putSerializable("yourkey",dataToSave); 

}

при создании действия вызывается метод onCreate ().

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

savedInstanceState-это объект класса Bundle, который впервые имеет значение null, но содержит значения при его воссоздании. Чтобы сохранить состояние активности, вы должны переопределить данные метода onsaveinstance().

   @Override
    protected void onSaveInstanceState(Bundle outState) {
      outState.putString("key","Welcome Back")
        super.onSaveInstanceState(outState);       //save state
    }

поместите свои значения в объект пакета "outState", например outState.putString ("ключ"," Добро пожаловать обратно") и сохранить, вызвав super. Когда активность будет уничтожена, это состояние будет сохранено. Объект Bundle и может быть восстановлен после восстановления в onCreate () или onRestoreInstanceState (). Пакет, полученный в onCreate() и onRestoreInstanceState (), одинаковы.

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

          //restore activity's state
         if(savedInstanceState!=null){
          String reStoredString=savedInstanceState.getString("key");
            }
    }

или

  //restores activity's saved state
 @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
      String restoredMessage=savedInstanceState.getString("key");
    }

вот, комментарий с Стив Мозлиответ (by ToolmakerSteve), что ставит вещи в перспективу (в целом onSaveInstanceState vs onPause, east cost vs west cost saga)

@VVK-я частично не согласен. Некоторые способы выхода из приложения не срабатывают данные метода onsaveinstance (Осис). Это ограничивает полезность озис. Свой стоит поддерживать, для минимальных ресурсов ОС, но если приложение хочет вернуть пользователя в состоянии они были в, Независимо от того, как приложение было после выхода необходимо использовать постоянный подход к хранению. я использую onCreate для проверки пакета, и если он отсутствует, то проверьте постоянное хранение. Это централизует принятие решений. Я могу оправиться от аварии, или кнопку Exit или меню пункт Выход, или вернуться к экрану пользователя было на много дней позже. - ToolmakerSteve Sep 19 '15 в 10:38


Котлин код:

сохранить:

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

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


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

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

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


просто быстро решить эту проблему с помощью Пешне

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

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

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

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
}

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

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


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


добавление LiveData (компоненты архитектуры Android) в ваш проект

добавьте следующую зависимость

реализация " android.арка.жизненный цикл:расширения:1.1.0"

LiveData принимает наблюдателя и уведомляет его об изменениях данных только тогда, когда он находится в запущенном или возобновленном состоянии. Преимущество LiveData заключается в том, что когда ваша деятельность переходит в любое состояние, отличное от STARTED или возобновить он не будет называть событий onchanged метод обозреватель.

private TextView mTextView;
private MutableLiveData<String> mMutableLiveData;

@Override
protected void onCreate(Bundle savedInstanceState) {
    mTextView = (TextView) findViewById(R.id.textView);
    mMutableLiveData = new MutableLiveData<>();
    mMutableLiveData.observe(this, new Observer<String>() {
        @Override
        public void onChanged(@Nullable String s) {
            mTextView.setText(s);
        }
    });

}