Сохранение Android состояние активности с помощью Сохранить Instance государства

голоса
2k

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

Задан 30/09/2008 в 05:41
источник пользователем
На других языках...                            


27 ответов

голоса
2k

Вы должны переопределить 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.
}

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

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

Ответил 30/09/2008 в 07:12
источник пользователем

голоса
369

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

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

Ответил 30/09/2008 в 06:03
источник пользователем

голоса
358

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

В документе (в разделе «Деятельность в течение жизненного цикла»):

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

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

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

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

Ответил 26/05/2010 в 00:22
источник пользователем

голоса
167

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

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

Сохранение локальных данных varible / управления UI для жизни приложения (то есть временно) с помощью Instance State Bundle

[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 / управления пользовательского интерфейса между экземплярами приложений (т.е. постоянно), используя общие предпочтения

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

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

[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();
}
Ответил 27/08/2010 в 14:54
источник пользователем

голоса
125

Это классический «Гоча» развития Android. Есть две проблемы:

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

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

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

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

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

UPDATE июнь 2013 : Через несколько месяцев, я , наконец , нашел «правильное» решение. Вам не нужно управлять любым Stateful 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;
    }
}
Ответил 20/10/2012 в 00:47
источник пользователем

голоса
70

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

Ответил 07/05/2010 в 01:21
источник пользователем

голоса
59

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

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

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

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

Ответил 27/06/2011 в 17:17
источник пользователем

голоса
50

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

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

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

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

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

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

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

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

Ответил 05/02/2012 в 12:35
источник пользователем

голоса
50

Сохранение состояния является ляп в лучшем случае , насколько я понимаю. Если вам нужно сохранить постоянные данные, просто использовать 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 IGNORE  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;
Ответил 23/06/2011 в 18:07
источник пользователем

голоса
36

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

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

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

Ответил 17/01/2012 в 19:28
источник пользователем

голоса
35

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

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

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

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

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

Например, вот как вы можете восстановить некоторые данные о состоянии в 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 является недействительным:

  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);
}
Ответил 15/04/2016 в 04:13
источник пользователем

голоса
32

Действительно onSaveInstanceсостояние Каллен , когда активность идет на фоне

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

Ответил 23/05/2010 в 23:32
источник пользователем

голоса
28

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

Ответил 20/08/2015 в 02:04
источник пользователем

голоса
28

В то же время я вообще не больше пользы

Bundle savedInstanceState & Co

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

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

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

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

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

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

Ответил 31/03/2012 в 14:36
источник пользователем

голоса
25

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

Ваша активность будет воссоздана только с государственным расслоением, когда:

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

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

При тестировании привет мир пример есть несколько способов выйти и вернуться к деятельности.

  • При нажатии на кнопку назад Активность закончена. Повторно запускать приложение является новым экземпляром марки. Вы не возобновляя от фона на всех.
  • При нажатии на кнопку домой или использовать переключатель задач Активности будет идти в фоновом режиме. При переходе обратно в приложение OnCreate будет вызываться только если активность должна была быть уничтожена.

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

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

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

Ответил 21/01/2015 в 02:19
источник пользователем

голоса
23

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

Ответил 05/09/2012 в 09:27
источник пользователем

голоса
15

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

mySavedInstanceState=savedInstanceState;

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

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

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

Ответил 16/04/2014 в 18:11
источник пользователем

голоса
12

Хотя принятый ответ правильный, есть быстрый и простой способ сохранить состояние активности на Android с использованием библиотеки под названием щипцы для льда . Щипцы для льда является процессор аннотаций , который берет на себя все шаблонного кода , используемого в сохранении и восстановлении состояния для вас.

Делая какие-нибудь подобное с щипцы для льда:

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.

Ответил 26/01/2016 в 03:07
источник пользователем

голоса
12

Есть два основных способа реализации этих изменений.

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

}
Ответил 18/12/2015 в 11:43
источник пользователем

голоса
11

Когда создается деятельность это OnCreate () вызывается метод.

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

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

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

положить ваши ценности в «outState» объект Bundle как outState.putString ( «ключ», «Welcome Back») и сохранить с помощью вызова супер. Когда работа будет разрушена это состояние спасаются в объекте 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");
    }
Ответил 23/01/2017 в 10:29
источник пользователем

голоса
8

Вот комментарий от Steve Моз ответа «s (по ToolmakerSteve ) , что ставит вещи в перспективу (в целом onSaveInstanceState против OnPause, восток стоимости против запада стоимости саги)

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

Ответил 07/02/2017 в 15:56
источник пользователем

голоса
7

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

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

Ответил 28/09/2016 в 11:46
источник пользователем

голоса
6

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

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

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

Ответил 09/08/2016 в 12:57
источник пользователем

голоса
6

Простые быстро решить эту проблему с помощью щипцов для льда

Во-первых, установка библиотеки в 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);
  }
}

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

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

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
}
Ответил 08/08/2016 в 02:07
источник пользователем

голоса
5

Котлин код:

спасти:

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

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

Ответил 12/02/2018 в 19:42
источник пользователем

голоса
0

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

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

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

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

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

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

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

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

Более подробно о onSavedInstanceState (Bundle savedinstaneState) при сохранении данных

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

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

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

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

 onCreate(Bundle savedInstanceState)

ИЛИ ЖЕ

onRestoreInstanceState(Bundle savedInstanceState)

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

Более подробно о onRestoreInstanceState (Bundle savedinstaneState)

@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)Вызывается системой только тогда , когда пользователь хочет вернуться к деятельности. Например, вы используете App X и вдруг вы получаете звонок. Вы двигаетесь к приложению вызывающего абонента и вернуться в приложении X. В этом случае onSaveInstanceState(Bundle savedInstanceState)метод будет вызван.

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

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

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

Ответил 10/01/2019 в 08:48
источник пользователем

голоса
0

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

Ответил 27/08/2018 в 11:51
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more