vida una savedinstancestate example estados estado ciclo app activity actividad android android-activity application-state

una - Guardar el estado de la actividad de Android utilizando Guardar estado de instancia



save activity state android (27)

Agregar LiveData (componentes de la arquitectura de Android) a su proyecto

agregue la siguiente dependencia

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

LiveData toma un observador y le notifica sobre los cambios en los datos solo cuando está en estado INICIADO o REANUDADO. La ventaja con LiveData es que cuando su actividad entra en cualquier estado distinto de INICIADO o REANUDADO , no llamará al método onChanged en el observador .

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

He estado trabajando en la plataforma de Android SDK, y no está nada claro cómo guardar el estado de una aplicación. Entonces, dado este pequeño cambio de herramientas del ejemplo ''Hola, 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); } }

Pensé que sería suficiente para el caso más simple, pero siempre responde con el primer mensaje, sin importar cómo me aleje de la aplicación.

Estoy seguro de que la solución es tan simple como anular onPause o algo así, pero he estado hurgando en la documentación durante 30 minutos aproximadamente y no he encontrado nada obvio.


Ambos métodos son útiles y válidos, y ambos son los más adecuados para diferentes escenarios:

  1. El usuario finaliza la aplicación y la vuelve a abrir en una fecha posterior, pero la aplicación necesita volver a cargar los datos de la última sesión; esto requiere un enfoque de almacenamiento persistente, como el uso de SQLite.
  2. El usuario cambia de aplicación y luego regresa al original y desea continuar donde lo dejó: guardar y restaurar los datos del paquete (como los datos del estado de la aplicación) en onSaveInstanceState() y onRestoreInstanceState() es generalmente adecuado.

Si guarda los datos de estado de forma persistente, puede volver a cargarlos en onResume() o onCreate() (o en realidad en cualquier llamada de ciclo de vida). Este comportamiento puede o no ser deseado. Si lo almacena en un paquete en un estado de InstanceState , entonces es transitorio y solo es adecuado para almacenar datos para su uso en la misma ''sesión'' del usuario (uso el término sesión de manera suelta) pero no entre ''sesiones''.

No es que un enfoque sea mejor que el otro, como todo, es importante entender qué comportamiento se requiere y seleccionar el enfoque más apropiado.


Creo que encontré la respuesta. Déjame decirte lo que he hecho en palabras simples:

Supongamos que tengo dos actividades, actividad1 y actividad2 y estoy navegando de actividad1 a actividad2 (he realizado algunos trabajos en la actividad2) y de nuevo a la actividad 1 haciendo clic en un botón en actividad1. Ahora, en esta etapa, quería volver a la actividad2 y quiero ver mi actividad2 en las mismas condiciones en las que dejé la actividad2 por última vez.

Para el escenario anterior, lo que he hecho es que en el manifiesto hice algunos cambios como este:

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

Y en la actividad1 del evento de clic de botón, he hecho lo siguiente:

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

Y en la actividad2 en el evento de clic de botón, lo he hecho así:

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

Ahora, lo que sucederá es que, independientemente de los cambios que hayamos realizado en la actividad2, no se perderán, y podemos ver la actividad2 en el mismo estado que dejamos anteriormente.

Creo que esta es la respuesta y esto funciona bien para mí. Corrígeme si estoy equivocado.


El estado de ahorro es, en el mejor de los casos, un gran problema en lo que a mí respecta. Si necesita guardar datos persistentes, solo use una SQLite datos SQLite . Android lo hace tan fácil.

Algo como esto:

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) { } } }

Una simple llamada después de eso

dataHelper dh = new dataHelper(getBaseContext()); String status = (String) dh.getCode("appState", "safetyDisabled"); Date serviceStart = (Date) dh.getCode("serviceStartTime", null); dh.close(); dh = null;


Este es un clásico ''gotcha'' del desarrollo de Android. Hay dos problemas aquí:

  • Existe un error sutil de Android Framework que complica enormemente la administración de la pila de aplicaciones durante el desarrollo, al menos en versiones heredadas (no estoy completamente seguro de si / cuándo / cómo se solucionó). Voy a discutir este error a continuación.
  • La forma "normal" o prevista para gestionar este problema es, en sí misma, bastante complicada con la dualidad de onPause / onResume y onSaveInstanceState / onRestoreInstanceState

Al examinar todos estos hilos, sospecho que la mayoría de los desarrolladores de tiempo hablan de estos dos temas diferentes simultáneamente ... por lo tanto, toda la confusión y los informes de "esto no funciona para mí".

Primero, para aclarar el comportamiento "deseado": onSaveInstance y onRestoreInstance son frágiles y solo para el estado transitorio. El uso previsto (afaict) es manejar la recreación de actividad cuando se gira el teléfono (cambio de orientación). En otras palabras, el uso previsto es cuando su Actividad todavía está lógicamente "en la parte superior", pero aún debe ser reenlazada por el sistema. El paquete guardado no se conserva fuera del proceso / memoria / gc, por lo que realmente no puede confiar en esto si su actividad pasa al fondo. Sí, tal vez la memoria de su Actividad sobrevivirá a su viaje al fondo y escape al GC, pero esto no es confiable (ni es previsible).

Entonces, si tiene un escenario en el que hay un ''progreso'' o estado significativo que debe persistir entre los ''lanzamientos'' de su aplicación, la guía es usar onPause y onResume. Debes elegir y preparar una tienda persistente tú mismo.

PERO, hay un error muy confuso que complica todo esto. Los detalles están aquí:

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

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

Básicamente, si su aplicación se inicia con la marca SingleTask y luego la lanza desde la pantalla de inicio o desde el menú de inicio, entonces esa invocación posterior creará una NUEVA tarea ... efectivamente tendrá dos instancias diferentes de su aplicación habitando la misma pila ... lo que se vuelve muy extraño muy rápido. Esto parece suceder cuando inicias tu aplicación durante el desarrollo (es decir, desde Eclipse o Intellij), por lo que los desarrolladores se encuentran con esto mucho. Pero también a través de algunos de los mecanismos de actualización de la tienda de aplicaciones (por lo que también afecta a sus usuarios).

Luché a través de estos hilos durante horas antes de darme cuenta de que mi principal problema era este error, no el comportamiento de marco previsto. Un gran relato y solución (ACTUALIZACIÓN: ver más abajo) parece ser del usuario @kaciula en esta respuesta:

Tecla de inicio con el comportamiento

ACTUALIZACIÓN Junio ​​de 2013 : Meses después, finalmente encontré la solución ''correcta''. No es necesario que gestione ninguna marca de aplicación de inicio con estado, puede detectar esto desde el marco y rescatarla adecuadamente. Lo uso cerca del comienzo de mi 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; } }


Mi colega escribió un artículo que explica el estado de la aplicación en dispositivos Android, que incluye explicaciones sobre el ciclo de vida de la actividad y la información del estado, cómo almacenar la información del estado, y cómo guardar en State Bundle y SharedPreferences y puede consultar aquí .

El artículo cubre tres enfoques:

Almacene datos de control de UI / variable locales para el tiempo de vida de la aplicación (es decir, temporalmente) utilizando el paquete de estado de instancia

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

Almacene datos de control de UI / variable locales entre instancias de la aplicación (es decir, de forma permanente) utilizando Preferencias Compartidas

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

Mantener vivas las instancias de objetos en la memoria entre las actividades dentro del tiempo de vida de la aplicación utilizando la instancia retenida sin configuración

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


Se llama a onSaveInstanceState cuando el sistema necesita memoria y mata una aplicación. No se llama cuando el usuario simplemente cierra la aplicación. Así que creo que el estado de la aplicación también debería guardarse en onPause Debería guardarse en un almacenamiento persistente como Preferences o Sqlite


Tenga en cuenta que NO es seguro usar onSaveInstanceState y onRestoreInstanceState para datos persistentes , de acuerdo con la documentación sobre los estados de Actividad en developer.android.com/reference/android/app/Activity.html .

Los estados del documento (en la sección ''Ciclo de vida de la actividad''):

Tenga en cuenta que es importante guardar los datos persistentes en onPause() lugar de onSaveInstanceState(Bundle) porque el último no forma parte de las devoluciones de llamada del ciclo de vida, por lo que no se llamará en todas las situaciones como se describe en su documentación.

En otras palabras, ponga su código de guardar / restaurar para datos persistentes en onPause() y onResume() !

EDITAR : Para mayor aclaración, aquí está la documentación de onSaveInstanceState() :

Se llama a este método antes de que una actividad pueda ser eliminada para que cuando vuelva en algún momento en el futuro pueda restaurar su estado. Por ejemplo, si la actividad B se inicia frente a la actividad A, y en algún momento se cancela la actividad A para reclamar recursos, la actividad A tendrá la oportunidad de guardar el estado actual de su interfaz de usuario a través de este método para que cuando el usuario regrese a la actividad A, el estado de la interfaz de usuario se puede restaurar a través de onCreate(Bundle) o onRestoreInstanceState(Bundle) .


onSaveInstanceState() para datos transitorios (restaurado en onCreate() / onRestoreInstanceState() ), onPause() para datos persistentes (restaurado en onResume() ). Desde los recursos técnicos de Android:

Android llama a onSaveInstanceState () si la Actividad se está deteniendo y se puede eliminar antes de que se reanude. Esto significa que debe almacenar cualquier estado necesario para reinicializar a la misma condición cuando se reinicia la actividad. Es la contraparte del método onCreate (), y de hecho, el paquete savedInstanceState pasado a onCreate () es el mismo paquete que usted construye como outState en el método onSaveInstanceState ().

onPause () y onResume () también son métodos complementarios. onPause () siempre se llama cuando finaliza la Actividad, incluso si instigamos esa (con una llamada finish () por ejemplo). Usaremos esto para guardar la nota actual en la base de datos. Una buena práctica es liberar cualquier recurso que se pueda liberar durante un onPause () también, para usar menos recursos cuando está en el estado pasivo.


onSaveInstanceState(Bundle savedInstanceState) anular onSaveInstanceState(Bundle savedInstanceState) y escribir los valores de estado de la aplicación que desea cambiar en el parámetro Bundle esta manera:

@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. }

El paquete es esencialmente una forma de almacenar un mapa NVP ("Nombre-Par de valores"), y se pasará a onCreate() y también a onRestoreInstanceState() donde extraerá los valores como este:

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

Por lo general, usaría esta técnica para almacenar valores de instancia para su aplicación (selecciones, texto no guardado, etc.).


savedInstanceState es solo para guardar el estado asociado con una instancia actual de una actividad, por ejemplo, información de navegación o selección actual, de modo que si Android destruye y recrea una actividad, puede regresar como estaba antes. Consulte la documentación para onCreate y onSaveInstanceState

Para un estado más duradero, considere usar una base de datos SQLite, un archivo o preferencias. Ver Ahorro de estado persistente .


Recrear una actividad

Hay algunos escenarios en los que su actividad se destruye debido al comportamiento normal de la aplicación, como cuando el usuario presiona el botón Atrás o su actividad señala su propia destrucción al llamar finish() . El sistema también puede destruir su actividad si se detiene actualmente y no se ha utilizado durante mucho tiempo o si la actividad en primer plano requiere más recursos, por lo que el sistema debe cerrar los procesos en segundo plano para recuperar la memoria.

Cuando su activity se destruye porque el usuario presiona Atrás o la activity termina por sí misma, el concepto del sistema de esa instancia de Activity desaparece para siempre porque el comportamiento indica que la actividad ya no es necesaria. Sin embargo, si el sistema destruye la actividad debido a las restricciones del sistema (en lugar del comportamiento normal de la aplicación), aunque la instancia de Actividad real ya no existe, el sistema recuerda que existió de tal manera que si el usuario navega hacia ella, el sistema crea una nueva instancia de la actividad que utiliza un conjunto de datos guardados que describe el estado de la actividad cuando se destroyed . Los datos guardados que el sistema utiliza para restaurar el estado anterior se denominan "estado de instancia" y son una colección de pares de clave-valor almacenados en un objeto Bundle.

Para guardar datos adicionales sobre el estado de la actividad, debe anular el método de devolución de llamada onSaveInstanceState (). El sistema llama a este método cuando el usuario abandona su actividad y le pasa el objeto Bundle que se guardará en caso de que su actividad se destruya inesperadamente. Si el sistema debe volver a crear la instancia de actividad más adelante, pasa el mismo objeto Bundle a los onRestoreInstanceState() y onCreate() .

A medida que el sistema comienza a detener su actividad, invoca onSaveInstanceState() (1) para que pueda especificar datos de estado adicionales que desee guardar en caso de que se deba recrear la instancia de la actividad. Si la actividad se destruye y se debe volver a crear la misma instancia, el sistema pasa los datos de estado definidos en (1) tanto al método onCreate() (2) como al método onRestoreInstanceState() (3).

Guarda tu estado de Activity

A medida que su actividad comienza a detenerse, el sistema llama a Guardar Estado de estado de onSaveInstanceState() para que su actividad pueda guardar información de estado con una colección de pares clave-valor. La implementación predeterminada de este método guarda la información sobre el estado de la jerarquía de vista de la actividad, como el texto en un widget EditText o la posición de desplazamiento de un ListView .

Para guardar información de estado adicional para su actividad, debe implementar onSaveInstanceState() y agregar pares clave-valor al objeto Bundle. Por ejemplo:

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

Precaución: Siempre llame a la implementación de superclase de onSaveInstanceState() para que la implementación predeterminada pueda guardar el estado de la jerarquía de vistas.

Restaura tu estado de Activity

Cuando su actividad se vuelve a crear después de haber sido destruida anteriormente, puede recuperar su estado guardado del paquete que el sistema pasa su actividad. Tanto los métodos de devolución de llamada onCreate() como onRestoreInstanceState() reciben el mismo Bundle que contiene la información del estado de la instancia.

Debido a que se llama al método onCreate() si el sistema está creando una nueva instancia de su actividad o recreando una anterior, debe verificar si el estado del paquete es nulo antes de intentar leerlo. Si es nulo, entonces el sistema está creando una nueva instancia de la actividad, en lugar de restaurar una anterior que fue destruida.

Por ejemplo, aquí es cómo puede restaurar algunos datos de estado en 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 } }

En lugar de restaurar el estado durante onCreate() , puede elegir implementar onRestoreInstanceState() , que el sistema llama después del método onStart() . El sistema llama onRestoreInstanceState() solo si hay un estado guardado para restaurar, por lo que no es necesario verificar si el paquete es nulo:

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


Código Kotlin:

salvar:

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

y luego en onCreate()oonRestoreInstanceState()

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

Agrega valores predeterminados si no quieres tener Opcionales


Cuando se crea una actividad, se llama al método onCreate ().

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

savedInstanceState es un objeto de la clase Bundle que es nulo por primera vez, pero contiene valores cuando se recrea. Para guardar el estado de la actividad, debe anular onSaveInstanceState ().

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

ponga sus valores en el objeto del paquete "outState" como outState.putString ("key", "Welcome Back") y guárdelo llamando a super. Cuando se destruye la actividad, se guarda el estado en el objeto Bundle y se puede restaurar después de la recreación en onCreate () o onRestoreInstanceState (). El paquete recibido en onCreate () y onRestoreInstanceState () es el mismo.

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

o

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


Hay básicamente dos formas de implementar este cambio.

  1. utilizando onSaveInstanceState()y onRestoreInstanceState().
  2. En manifiesto android:configChanges="orientation|screenSize".

Realmente no recomiendo usar el segundo método. Como en una de mis experiencias, causaba que la mitad de la pantalla del dispositivo se volviera negra mientras giraba de vertical a horizontal y viceversa.

Utilizando el primer método mencionado anteriormente, podemos conservar los datos cuando se cambia la orientación o se produce cualquier cambio de configuración. Conozco una forma en la que puede almacenar cualquier tipo de datos dentro del objeto guardado de estado de instancia.

Ejemplo: considere un caso si desea persistir el objeto Json. crear una clase modelo con getters y setters.

class MyModel extends Serializable{ JSONObject obj; setJsonObject(JsonObject obj) { this.obj=obj; } JSONObject getJsonObject() return this.obj; } }

Ahora en su actividad en el método onCreate y onSaveInstanceState, haga lo siguiente. Se verá algo como esto:

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


Mientras tanto, en general no hago más uso.

Bundle savedInstanceState & Co

El ciclo en vivo es para la mayoría de las actividades demasiado complicado y no es necesario. Y el propio estado de Google, ni siquiera es confiable.

Mi manera es guardar cualquier cambio inmediatamente en las preferencias.

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

De alguna manera, las preferencias compartidas funcionan de manera similar a los paquetes. Y naturalmente, y al principio, tales valores tienen que ser rojos de las preferencias.

En el caso de datos complejos, puede usar Sqlite en lugar de usar preferencias.

Al aplicar este concepto, la actividad simplemente continúa utilizando el último estado guardado, independientemente de si fue una apertura inicial con reinicios intermedios o una reapertura debida a la pila trasera.


Ahora Android proporciona ViewModels para guardar el estado, debe intentar usar eso en lugar de saveInstanceState.


Aquí hay un comentario de la respuesta de Steve Moseley (por ToolmakerSteve ) que pone las cosas en perspectiva (en general enSaveInstanceState vs onPause, East Cost vs West Cost Saga)

@VVK - Estoy parcialmente en desacuerdo. Algunas formas de salir de una aplicación no activan onSaveInstanceState (oSIS). Esto limita la utilidad de oSIS. Vale la pena admitirlo, para un mínimo de recursos del sistema operativo, pero si una aplicación quiere devolver al usuario al estado en el que se encontraba, independientemente de cómo se cerró la aplicación, es necesario utilizar un enfoque de almacenamiento persistente. Utilizo onCreate para verificar el paquete, y si falta, luego verifico el almacenamiento persistente. Esto centraliza la toma de decisiones. Puedo recuperarme de un bloqueo, salir del botón Atrás o salir del menú personalizado Salir, o volver a la pantalla en la que el usuario se encontraba muchos días después. - ToolmakerSteve Sep 19 ''15 a las 10:38


Aunque la respuesta aceptada es correcta, existe un método más rápido y sencillo para guardar el estado de la actividad en Android mediante una biblioteca llamada Icepick . Icepick es un procesador de anotaciones que se encarga de todo el código de repetición utilizado para guardar y restaurar el estado.

Haciendo algo así con Icepick:

class MainActivity extends Activity { @State String username; // These will be automatically saved and restored @State String password; @State int age; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Icepick.restoreInstanceState(this, savedInstanceState); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Icepick.saveInstanceState(this, outState); } }

Es lo mismo que hacer esto:

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 trabajará con cualquier objeto que guarde su estado con un Bundle.


Los métodos onSaveInstanceState(bundle)y onRestoreInstanceState(bundle)son útiles para la persistencia de datos simplemente mientras se gira la pantalla (cambio de orientación).
Ni siquiera son buenas, mientras que el cambio entre aplicaciones (ya que el onSaveInstanceState()se llama método, pero onCreate(bundle)y onRestoreInstanceState(bundle)no se invoca de nuevo.
Para un uso más persistencia preferencias compartidas. Leer este artículo


Mi problema fue que necesitaba persistencia solo durante el tiempo de vida de la aplicación (es decir, una ejecución única que incluye iniciar otras subactividades dentro de la misma aplicación y rotar el dispositivo, etc.). Probé varias combinaciones de las respuestas anteriores, pero no obtuve lo que quería en todas las situaciones. Al final, lo que funcionó para mí fue obtener una referencia a savedInstanceState durante onCreate:

mySavedInstanceState=savedInstanceState;

y úselo para obtener el contenido de mi variable cuando lo necesite, siguiendo las líneas de:

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

Utilizo onSaveInstanceStatey onRestoreInstanceStatecomo se sugirió anteriormente, pero supongo que también podría o alternativamente usar mi método para guardar la variable cuando cambie (por ejemplo, usando putBoolean)


No estoy seguro de si mi solución está mal vista o no, pero uso un servicio vinculado para mantener el estado de ViewModel. Si lo almacena en la memoria en el servicio o si persiste y recupera de una base de datos SqlLite depende de sus requisitos. Esto es lo que hacen los servicios de cualquier tipo, brindan servicios como el mantenimiento del estado de la aplicación y la lógica común de negocios abstracta.

Debido a las limitaciones de memoria y procesamiento inherentes a los dispositivos móviles, trato las vistas de Android de manera similar a una página web. La página no mantiene el estado, es puramente un componente de la capa de presentación cuyo único propósito es presentar el estado de la aplicación y aceptar la entrada del usuario. Las tendencias recientes en la arquitectura de aplicaciones web emplean el uso del antiguo modelo Modelo, Vista, Controlador (MVC), donde la página es la Vista, los datos del Dominio son el modelo y el controlador se encuentra detrás de un servicio web. El mismo patrón se puede emplear en Android con la Vista en buen estado ... la Vista, el modelo son los datos de su dominio y el Controlador se implementa como un servicio vinculado a Android. Cuando quiera que una vista interactúe con el controlador, enlácela al iniciar / reanudar y desvincular en detener / pausar.

Este enfoque le brinda la ventaja adicional de imponer el principio de diseño de Separación de Preocupación en el sentido de que todas las aplicaciones de la lógica de negocios pueden moverse a su servicio, lo que reduce la lógica duplicada en múltiples vistas y permite que la vista aplique otro principio de diseño importante, la Responsabilidad Única.


Para ayudar a reducir la plantilla, uso lo siguiente interfacey classpara leer / escribir en un Bundleestado de instancia de guardado.

Primero, cree una interfaz que se usará para anotar sus variables de instancia:

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 { }

Luego, cree una clase donde se usará la reflexión para guardar valores en el paquete:

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

Ejemplo de uso:

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

Nota: este código fue adaptado de un proyecto de biblioteca llamado AndroidAutowire que está licenciado bajo la licencia MIT .


Para obtener datos de estado de actividad almacenados onCreate(), primero debe guardar los datos en savedInstanceState mediante el SaveInstanceState(Bundle savedInstanceState)método de anulación .

Cuando SaveInstanceState(Bundle savedInstanceState)se llama al método de destrucción de actividad y se guardan los datos que desea guardar. Y obtienes lo mismo onCreate()cuando se reinicia la actividad. (SavedInstanceState no será nulo ya que has guardado algunos datos antes de que se destruya la actividad)


Para responder a la pregunta original directamente. savedInstancestate es nulo porque su actividad nunca se vuelve a crear.

Su actividad solo se volverá a crear con un paquete estatal cuando:

  • Los cambios de configuración, como cambiar la orientación o el idioma del teléfono, pueden requerir la creación de una nueva instancia de actividad.
  • Volverá a la aplicación desde el fondo después de que el sistema operativo haya destruido la actividad.

Android destruirá las actividades en segundo plano cuando esté bajo presión de memoria o después de haber estado en segundo plano durante un período prolongado de tiempo.

Al probar el ejemplo de "hola mundo", hay algunas formas de irse y volver a la actividad.

  • Cuando se presiona el botón Atrás, la actividad ha finalizado. Relanzar la aplicación es una nueva instancia. No estás reanudando desde el fondo en absoluto.
  • Cuando presiona el botón de inicio o usa el conmutador de tareas, la Actividad pasará a un segundo plano. Cuando se navegue de regreso a la aplicación, onCreate solo será llamado si la Actividad tuvo que ser destruida.

En la mayoría de los casos, si presiona Inicio y luego vuelve a iniciar la aplicación, no será necesario volver a crear la actividad. Ya existe en la memoria por lo que no se llamará a onCreate ().

Hay una opción en Configuración -> Opciones de desarrollador llamada "No guardar actividades". Cuando está habilitado, Android siempre destruirá las actividades y las volverá a crear cuando estén en segundo plano. Esta es una gran opción para dejarlo habilitado cuando se desarrolla porque simula el peor escenario. (Un dispositivo de memoria baja reciclando tus actividades todo el tiempo).

Las otras respuestas son valiosas porque le enseñan las formas correctas de almacenar el estado, pero no sentí que realmente respondieran POR QUÉ su código no estaba funcionando como esperaba.


Realmente decir onSaveInstancecallen cuando la actividad pasa a segundo plano

Cita de la documentación: "el método onSaveInstanceState(Bundle)se llama antes de colocar la actividad en ese estado de fondo"


Simple rápido para resolver este problema es usar Icepick

Primero, configura la biblioteca en app/build.gradle

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

Ahora, veamos este ejemplo a continuación, cómo guardar el estado en la Actividad.

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

Funciona para Actividades, Fragmentos o cualquier objeto que necesite serializar su estado en un Bundle (por ejemplo, ViewPresenters de mortero)

Icepick también puede generar el código de estado de instancia para vistas personalizadas:

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 }