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

2346

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

  • 4
    Когда saveInstanceState == null, а когда нет?
  • 74
    Вы явно уничтожаете свою деятельность, как вы сказали, удаляясь от нее, например, нажимая назад. На самом деле, сценарий, в котором используется этот «saveInstanceState», - это когда Android уничтожает вашу деятельность для отдыха. Для примера: если вы измените язык своего телефона во время выполнения действия (и поэтому необходимо загружать ресурсы, отличные от вашего проекта). Другой очень распространенный сценарий - это когда вы поворачиваете телефон в сторону, чтобы упражнение было воссоздано и отображено в альбомной ориентации.
Показать ещё 4 комментария
Теги:
android-activity
application-state

29 ответов

2481
Лучший ответ

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

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
  super.onSaveInstanceState(savedInstanceState);
  // Save UI state changes to the savedInstanceState.
  // This bundle will be passed to onCreate if the process is
  // killed and restarted.
  savedInstanceState.putBoolean("MyBoolean", true);
  savedInstanceState.putDouble("myDouble", 1.9);
  savedInstanceState.putInt("MyInt", 1);
  savedInstanceState.putString("MyString", "Welcome back to Android");
  // etc.
}

The Bundle - это, по существу, способ хранения карты NVP ( "Ценность имени" ), и она будет передана в onCreate(), а также onRestoreInstanceState(), где вы извлечете такие значения:

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
  super.onRestoreInstanceState(savedInstanceState);
  // Restore UI state from the savedInstanceState.
  // This bundle has also been passed to onCreate.
  boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
  double myDouble = savedInstanceState.getDouble("myDouble");
  int myInt = savedInstanceState.getInt("MyInt");
  String myString = savedInstanceState.getString("MyString");
}

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

  • 0
    В onRestoreInstanceState, я думаю, вы подразумеваете, что первая строка будет super.onRestoreInstanceState, а не super.onSaveInstanceState
  • 19
    Есть ли шанс, что это работает на телефоне, но не в эмуляторе? Кажется, я не могу получить ненулевое saveInstanceState.
Показать ещё 33 комментария
413

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

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

  • 2
    Когда saveInstanceState == null, а когда нет?
  • 5
    Параметр saveInstanceState имеет значение NULL, когда система создает новый экземпляр вашей Деятельности, а не NULL, когда он восстанавливает.
Показать ещё 1 комментарий
361

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

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

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

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

РЕДАКТИРОВАТЬ. Для дальнейшего уточнения здесь onSaveInstanceState() документация:

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

  • 50
    Просто чтобы придираться: это тоже небезопасно. Это зависит только от того, что вы хотите сохранить и как долго, о чем @Bernard не совсем ясно в своем первоначальном вопросе. InstanceState идеально подходит для сохранения текущего состояния пользовательского интерфейса (данные, введенные в элементы управления, текущие позиции в списках и т. Д.), Тогда как пауза / возобновление - единственная возможность для долговременного постоянного хранения.
  • 28
    Это должно быть понижено. Не безопасно использовать в (Save | Restore) InstanceState как методы жизненного цикла (т.е. делать в них что-то еще, кроме сохранения / восстановления состояния). Они отлично подходят для сохранения / восстановления состояния. Кроме того, как вы хотите сохранить / восстановить состояние в onPause и onResume? Вы не получаете Bundles в тех методах, которые можете использовать, поэтому вам придется использовать другое сохранение состояния в базах данных, файлах и т. Д., Что глупо.
Показать ещё 13 комментариев
171

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

В статье рассматриваются три подхода:

Храните локальные переменные/данные управления пользовательским интерфейсом для времени жизни приложения (т.е. временно), используя пакет состояния экземпляра

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

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

[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();
}
  • 2
    @ MartinBelcher-Eigo В статье говорится о данных в SharedPreferences: «Эти данные записываются в базу данных на устройстве.» Я считаю, что данные хранятся в файле в каталоге приложения файловой системы.
  • 1
    Данные @Tom SharefPrefs записываются в XML-файл. Является ли xml базой данных? Я бы сказал, что это;)
Показать ещё 2 комментария
131

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

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

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

Во-первых, чтобы прояснить "предполагаемое" поведение: onSaveInstance и onRestoreInstance являются хрупкими и только для переходного состояния. Предполагаемое использование (afaict) - это обработка активности при повороте телефона (изменение ориентации). Другими словами, предполагаемое использование - это когда ваша активность по-прежнему логически "сверху", но все же должна быть восстановлена ​​системой. Сохраненный пакет не сохраняется за пределами процесса/памяти/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), поэтому разработчики часто сталкиваются с этим. Но также через некоторые механизмы обновления магазина приложений (так что это также влияет на ваших пользователей).

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

Домашнее нажатие клавиш

ОБНОВЛЕНИЕ Июнь 2013. Через несколько месяцев я наконец нашел правильное решение. Вам не нужно самостоятельно управлять любыми флагами с установленными состояниями, а вы можете обнаружить это из фреймворка и поручиться соответствующим образом. Я использую это в начале моего 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;
    }
}
  • 2
    Почему Android снова стала мобильной ОС второго выбора? зарывает голову в руки
69

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

  • 33
    Извините, это не совсем правильно. onSaveInstanceState вызывается до того, как действие должно быть повторно выполнено. т.е. каждый раз, когда пользователь поворачивает устройство. Он предназначен для хранения переходных состояний просмотра. Когда android заставляет приложение закрываться, onSaveInstanceState фактически НЕ вызывается (именно поэтому хранение важных данных приложения небезопасно). Однако onPause гарантированно вызывается до прекращения действия, поэтому его следует использовать для сохранения постоянной информации в настройках или в Squlite. Правильный ответ, неправильные причины.
  • 1
    @ moveaway00 - частично неверно. Если система убивает приложение , чтобы восстановить ресурсы, это называют onSaveInstanceState. От developer.android.com/training/basics/activity-lifecycle/… - «Система также может уничтожить вашу деятельность ... для выполнения операций на переднем плане требуется больше ресурсов, поэтому система должна завершить фоновые процессы для восстановления памяти. ... если система разрушает действие из-за системных ограничений ... система запоминает, что оно существовало ... Сохраненные данные ... представляют собой набор пар ключ-значение, хранящихся в объекте Bundle. ".
64

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

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

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

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

  • 1
    Как мы выбираем между SQLite и Preferences при поиске постоянного хранилища?
51

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

Что-то вроде этого:

import java.util.Date;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class dataHelper {

    private static final String DATABASE_NAME = "autoMate.db";
    private static final int DATABASE_VERSION = 1;

    private Context context;
    private SQLiteDatabase db;
    private OpenHelper oh ;

    public dataHelper(Context context) {
        this.context = context;
        this.oh = new OpenHelper(this.context);
        this.db = oh.getWritableDatabase();
    }

    public void close()
    {
        db.close();
        oh.close();
        db = null;
        oh = null;
        SQLiteDatabase.releaseMemory();
    }


    public void setCode(String codeName, Object codeValue, String codeDataType)
    {
        Cursor codeRow = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        String cv = "" ;

        if (codeDataType.toLowerCase().trim().equals("long") == true)
        {
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            cv = String.valueOf(((Date)codeValue).getTime());
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            String.valueOf(codeValue);
        }
        else
        {
            cv = String.valueOf(codeValue);
        }

        if(codeRow.getCount() > 0) //exists-- update
        {
            db.execSQL("update code set codeValue = '" + cv +
                "' where codeName = '" + codeName + "'");
        }
        else // does not exist, insert
        {
            db.execSQL("INSERT INTO code (codeName, codeValue, codeDataType) VALUES(" +
                    "'" + codeName + "'," +
                    "'" + cv + "'," +
                    "'" + codeDataType + "')" );
        }
    }

    public Object getCode(String codeName, Object defaultValue)
    {
        //Check to see if it already exists
        String codeValue = "";
        String codeDataType = "";
        boolean found = false;
        Cursor codeRow  = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        if (codeRow.moveToFirst())
        {
            codeValue = codeRow.getString(codeRow.getColumnIndex("codeValue"));
            codeDataType = codeRow.getString(codeRow.getColumnIndex("codeDataType"));
            found = true;
        }

        if (found == false)
        {
            return defaultValue;
        }
        else if (codeDataType.toLowerCase().trim().equals("long") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (long)0;
            }
            return Long.parseLong(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (int)0;
            }
            return Integer.parseInt(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            if (codeValue.equals("") == true)
            {
                return null;
            }
            return new Date(Long.parseLong(codeValue));
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            if (codeValue.equals("") == true)
            {
                return false;
            }
            return Boolean.parseBoolean(codeValue);
        }
        else
        {
            return (String)codeValue;
        }
    }


    private static class OpenHelper extends SQLiteOpenHelper {

        OpenHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE IF  NOT EXISTS code" +
            "(id INTEGER PRIMARY KEY, codeName TEXT, codeValue TEXT, codeDataType TEXT)");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }
}

Простой вызов после этого

dataHelper dh = new dataHelper(getBaseContext());
String status = (String) dh.getCode("appState", "safetyDisabled");
Date serviceStart = (Date) dh.getCode("serviceStartTime", null);
dh.close();
dh = null;
  • 5
    Поскольку загрузка базы данных SQLite занимает слишком много времени, учитывая, что это критический путь к отображению пользовательского интерфейса приложения. Я на самом деле не рассчитал время, так что я рад, что поправился, но наверняка загрузка и открытие файла базы данных не будут быстрыми?
  • 1
    Большое спасибо за предоставление решения, которое новичок может вырезать и вставлять в свое приложение и использовать прямо сейчас! @Tom. Для сохранения скорости требуется около семи секунд, чтобы сохранить 1000 пар, но вы можете сделать это в AsyncTask. Тем не менее, вам нужно добавить finally {cursor.close ()}, иначе при этом произойдет сбой из-за утечки памяти.
Показать ещё 1 комментарий
48

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

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

В приведенном выше сценарии я сделал то, что в манифесте я сделал некоторые изменения следующим образом:

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

И в действии1 на событии нажатия кнопки я сделал вот так:

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 в том же состоянии, в котором мы ранее были.

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

  • 1
    @bagusflyer заботиться, чтобы быть более конкретным ??? Ваш комментарий бесполезен, и никто не может помочь вам, основываясь на этом.
  • 1
    Это ответ на другую ситуацию: два действия в одном приложении. ОП означает выход из приложения (например, кнопка «Домой» или другие средства для переключения на другое приложение).
39

Восстанавливая активность

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

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

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

Когда система начинает останавливать вашу деятельность, она вызывает onSaveInstanceState() (1), чтобы вы могли указать дополнительные данные состояния, которые вы хотите сохранить, в случае, если экземпляр Activity должен быть воссоздан. Если активность уничтожена и один и тот же экземпляр должен быть воссоздан, система передает данные состояния, определенные в (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 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() вызывается, создает ли система новый экземпляр вашей деятельности или воссоздает предыдущий, вы должны проверить, не является ли пул состояний пустым, прежде чем вы попытаетесь его прочитать. Если он равен нулю, система создает новый экземпляр действия вместо восстановления предыдущего, который был уничтожен.

Например, здесь вы можете восстановить некоторые данные состояния в 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() только в том случае, если для восстановления требуется сохраненное состояние, поэтому вам не нужно проверять, является ли Bundle равным 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);
}
35

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

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

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

33

Действительно onSaveInstance state callen, когда Activity переходит в фоновый режим

Цитата из документов: "метод onSaveInstanceState(Bundle) вызывается перед тем, как помещать активность в такое фоновое состояние"

29

Чтобы помочь уменьшить шаблон, я использую следующие 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.

29

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

Bundle savedInstanceState & Co

Жизненный цикл для большинства видов деятельности слишком сложен и не нужен.

И Google заявляет о себе, это даже не надежно.

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

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

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

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

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

27

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

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

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

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

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

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

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

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

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

22

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

  • 2
    В вашем случае onCreate и onRestoreInstanceState не называют , потому что Activity не разрушается вообще при переключении приложений, так что нет никакой необходимости , чтобы восстановить что - либо. Android вызывает onSaveInstanceState на тот случай, если действие будет уничтожено позже (что происходит при 100% -ной уверенности при повороте экрана, потому что изменилась вся конфигурация устройства, и действие должно быть воссоздано с нуля).
15

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

mySavedInstanceState=savedInstanceState;

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

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

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

14

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

Выполнение чего-то подобного Icecick:

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

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

12

Когда создается действие, вызывается метод onCreate().

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

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

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

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

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

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

или

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

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

  • с помощью onSaveInstanceState() и onRestoreInstanceState().
  • В манифесте android:configChanges="orientation|screenSize".

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

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

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

class MyModel extends Serializable{
JSONObject obj;

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

JSONObject getJsonObject()
return this.obj;
} 
}

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

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

}
8

Вот комментарий от ответа Steve Moseley (ToolmakerSteve), который ставит все в перспективе (в целом onSaveInstanceState vs onPause, восточная стоимость и западная сага)

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

7

Код Котлина:

сохранить:

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

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

5

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

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

5

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

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

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

5

Простое решение этой проблемы - 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);
  }
}

Он работает для операций, фрагментов или любого объекта, который должен сериализовать свое состояние на Bundle (например, 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
}
  • 0
    это работает для фрагмента? это сохраняет и восстанавливает состояние пользовательского представления?
  • 1
    @ralphspoon да, это работает для фрагмента и пользовательского просмотра. Пожалуйста, проверьте пример кода. Я отредактировал свой ответ. Я предлагаю вам перейти к официальным документам здесь github.com/frankiesardo/icepick, чтобы найти больше примеров кода.
Показать ещё 1 комментарий
1

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

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

implementation "android.arch.lifecycle:extensions:1.1.0"

LiveData принимает наблюдателя и уведомляет его об изменениях данных, только когда он находится в состоянии STARTED или RESUMED. Преимущество LiveData заключается в том, что когда ваша деятельность переходит в любое состояние, кроме STARTED или RESUMED, она не вызывает метод 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);
        }
    });

}
1

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

  • 2
    Это неправда. Из документации: «В отличие от сохраненного состояния экземпляра, ViewModel уничтожаются во время смерти процесса, инициированного системой. Вот почему вы должны использовать объекты ViewModel в сочетании с onSaveInstanceState () (или некоторой другой сохраняемостью диска), сохраняя идентификаторы в saveInstanceState для облегчения просмотра модели перезагружают данные после смерти системы ".
  • 0
    Просто столкнулся с этим с изменениями разрешений в фоновом режиме.
0

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

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

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

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

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

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

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

Например, int currentScore

Более подробная информация о onSavedInstanceState (Bundle saveinstaneState) при сохранении данных

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user 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)

ИЛИ ЖЕ

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).

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

Вот самая актуальная ссылка.

0

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

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
  super.onSaveInstanceState(savedInstanceState);
  // Save UI state changes to the savedInstanceState.
  // This bundle will be passed to onCreate if the process is
  // killed and restarted.
  savedInstanceState.putBoolean("MyBoolean", true);
  savedInstanceState.putDouble("myDouble", 1.9);
  savedInstanceState.putInt("MyInt", 1);
  savedInstanceState.putString("MyString", "Welcome back to Android");
  // etc.
}

The Bundle - это, по сути, способ хранения карты NVP ( "Ценность имени" ), и она будет передана в onCreate(), а также onRestoreInstanceState(), где вы извлечете такие значения:

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
  super.onRestoreInstanceState(savedInstanceState);
  // Restore UI state from the savedInstanceState.
  // This bundle has also been passed to onCreate.
  boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
  double myDouble = savedInstanceState.getDouble("myDouble");
  int myInt = savedInstanceState.getInt("MyInt");
  String myString = savedInstanceState.getString("MyString");
}

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню