¿Cómo guardar un estado de actividad usando guardar estado de instancia?

2620

He estado trabajando en la plataforma Android SDK, y no está claro cómo guardar el estado de una aplicación. Entonces, dada esta pequeña herramienta 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 navegue fuera de la aplicación.

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

Bernardo
fuente
99
¿Cuándo se guardaInstanceState == nulo y cuándo no es nulo?
Trojan.ZBOT
90
Estás destruyendo explícitamente tu actividad al, como dijiste, alejándote de ella, como presionando hacia atrás. En realidad, el escenario en el que se usa este 'saveInstanceState' es cuando Android destruye su actividad para la recreación. Por ejemplo: si cambia el idioma de su teléfono mientras se ejecutaba la actividad (y, por lo tanto, se deben cargar diferentes recursos de su proyecto). Otro escenario muy común es cuando gira el teléfono hacia un lado para que la actividad se vuelva a crear y se muestre en horizontal.
villoren
16
Para obtener el segundo mensaje, habilite "No mantener actividades" en las opciones de desarrollo. Presione un botón de inicio y regrese de los recientes.
Yaroslav Mytkalyk
55
Esto es bastante útil developer.android.com/training/basics/activity-lifecycle/…
Syed Raza Mehdi
66
puede hacerlo con: onSaveInstanceState (Bundle savedInstanceState)
VahidHoseini

Respuestas:

2568

Debe anular onSaveInstanceState(Bundle savedInstanceState)y escribir los valores de estado de la aplicación que desea cambiar al Bundleparámetro de 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 ("Par nombre-valor"), y se pasará a onCreate()y también onRestoreInstanceState()donde extraerá los valores de la actividad de esta manera:

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

O de un fragmento.

@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
    super.onViewStateRestored(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");
}

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

Reto Meier
fuente
24
¿Hay alguna posibilidad de que esto funcione en el teléfono, pero no en el emulador? Parece que no puedo obtener un saveInstanceState no nulo.
Adam Jack
491
CUIDADO: debe llamar a super.onSaveInstanceState (savedInstanceState) antes de agregar sus valores al paquete, o se eliminarán en esa llamada (Droid X Android 2.2).
jkschneider
121
Cuidado: la documentación oficial establece que debe guardar información importante dentro del método onPause porque el método onsaveinstance no forma parte del ciclo de vida de Android. developer.android.com/reference/android/app/Activity.html
schlingel
32
Ese hecho efectivamente se vuelve onSaveInstanceStatecasi inútil, excepto solo en el caso de cambios en la orientación de la pantalla. En casi todos los demás casos, nunca puede confiar en él y tendrá que guardar manualmente su estado de IU en otro lugar. O evitar que su aplicación sea eliminada anulando el comportamiento del botón ATRÁS. No entiendo por qué incluso lo implementaron así en primer lugar. Totalmente poco intuitivo. Y no puede tener ese paquete que le proporciona el sistema para guardar cosas, excepto en este método muy particular.
chakrit
12
Tenga en cuenta que guardar / restaurar el estado del interfaz de usuario a / desde el paquete es automáticamente atendidos por Views que han sido los identificadores asignados . De los onSaveInstanceStatedocumentos: "La implementación predeterminada se encarga de la mayor parte del estado de UI por instancia llamando onSaveInstanceState()a cada vista en la jerarquía que tiene una identificación y guardando la identificación de la vista actualmente enfocada (todo lo cual se restaura por la implementación predeterminada de onRestoreInstanceState(Bundle)) "
Vicky Chijwani
433

Esto savedInstanceStatees 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 volver como estaba antes. Consulte la documentación para onCreateyonSaveInstanceState

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

Dave L.
fuente
3
¿Cuándo se guardaInstanceState == nulo y cuándo no es nulo?
Trojan.ZBOT
66
savedInstanceState es nulo cuando el sistema está creando una nueva instancia de su Actividad y no es nulo cuando se está restaurando.
Gabriel Câmara
77
... lo que plantea la pregunta de cuándo necesita el sistema crear una nueva instancia de Actividad. Algunas formas de salir de una aplicación no crean un paquete, por lo que se debe crear una nueva instancia. Este es el problema fundamental; significa que uno no puede confiar en la existencia del paquete y debe hacer algunos medios alternativos de almacenamiento persistente. El beneficio de onSave / onRestoreInstanceState es que es un mecanismo que el sistema puede hacer abruptamente , sin consumir muchos recursos del sistema. Por lo tanto, es bueno admitir eso, así como tener almacenamiento persistente para una salida más elegante de la aplicación.
ToolmakerSteve
415

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

El documento establece (en la sección 'Ciclo de vida de la actividad'):

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

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

EDITAR : Para más aclaraciones, aquí está la onSaveInstanceState()documentación:

Este método se llama antes de que una actividad pueda ser eliminada para que cuando regrese 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 la actividad A se elimina 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 puede restaurarse mediante onCreate(Bundle)o onRestoreInstanceState(Bundle).

Steve Moseley
fuente
55
Solo para criticar: tampoco es inseguro. Esto solo depende de lo que desee conservar y durante cuánto tiempo, lo que @Bernard no tiene del todo claro en su pregunta original. InstanceState es perfecto para preservar el estado actual de la interfaz de usuario (datos ingresados ​​en controles, posiciones actuales en listas, etc.), mientras que Pausa / Reanudar es la única posibilidad de almacenamiento persistente a largo plazo.
Pontus Gagge
30
Esto debería ser rechazado. No es seguro usarlo en (Guardar | Restaurar) InstanceState como los métodos de ciclo de vida (es decir, hacer cualquier otra cosa en ellos que no sea guardar / restaurar el estado). Son perfectamente buenos para guardar / restaurar el estado. Además, ¿cómo desea guardar / restaurar el estado en onPause y onResume? No obtienes paquetes en esos métodos que puedes usar, por lo que tendrías que emplear algún otro estado de ahorro, en bases de datos, archivos, etc., que es estúpido.
Felix
141
No deberíamos rechazar a esta persona, al menos hizo esfuerzos para revisar la documentación y creo que las personas estamos aquí para construir una comunidad bien informada y ayudarnos mutuamente a NO BAJAR EL VOTO. así que voto por el esfuerzo y les pediré a las personas que no voten en lugar de votar o que no voten ... esta persona aclara la confusión que a uno le gustaría tener cuando revisa la documentación. 1 voto arriba :)
AZ_
21
No creo que esta respuesta merezca un voto negativo. Al menos hizo un esfuerzo por responder y había citado una sección de doco.
GSree
34
¡Esta respuesta es absolutamente correcta y merece el voto UP, no down! Permítanme aclarar la diferencia entre estados para aquellos tipos que no lo ven. Un estado de GUI, como botones de radio seleccionados y algo de texto en el campo de entrada, es mucho menos importante que el estado de datos, como los registros agregados a una lista que se muestra en un ListView. Este último debe almacenarse en la base de datos en onPause porque es la única llamada garantizada. Si lo coloca en onSaveInstanceState, corre el riesgo de perder datos si no se llama. Pero si la selección del botón de opción no se guarda por la misma razón, no es gran cosa.
JBM
206

Mi colega escribió un artículo explicando el estado de la aplicación en dispositivos Android, incluyendo explicaciones sobre el ciclo de vida de la actividad y la información sobre el estado, cómo almacenar información sobre el estado Bundley cómo guardarla en el estado SharedPreferencesy echar un vistazo aquí .

El artículo cubre tres enfoques:

Almacene datos de control de UI / variables locales para la vida útil de la aplicación (es decir, temporalmente) utilizando un 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 / variables locales entre instancias de aplicación (es decir, permanentemente) 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 instancias de objetos vivas en la memoria entre actividades dentro de la vida útil de la aplicación utilizando una 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();
}
Martin Belcher - AtWrk
fuente
3
@ MartinBelcher-Eigo El artículo dice acerca de los datos en SharedPreferences que "Estos datos se escriben en la base de datos en el dispositivo ..." Creo que los datos se almacenan en un archivo en el directorio de la aplicación del sistema de archivos.
Tom
2
Los datos de @Tom SharefPrefs se escriben en el archivo xml. ¿Es xml un tipo de base de datos? Yo diría que es;)
MaciejGórski
148

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

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

Navegando a través de todos estos hilos, sospecho que la mayoría de las veces los desarrolladores están hablando de estos dos problemas diferentes simultáneamente ... de ahí toda la confusión y los informes de "esto no funciona para mí".

Primero, para aclarar el comportamiento 'previsto': onSaveInstance y onRestoreInstance son frágiles y solo para el estado transitorio. El uso previsto (afaict) es manejar la recreación de la 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 reinstalada 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 a un segundo plano. Sí, quizás la memoria de su Actividad sobrevivirá a su viaje al fondo y escapará de la GC, pero esto no es confiable (ni es predecible).

Entonces, si tiene un escenario en el que hay un "progreso del usuario" significativo o un estado que debería persistir entre los "lanzamientos" de su aplicación, la guía es usar onPause y onResume. Debe elegir y preparar una tienda persistente usted 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 el indicador SingleTask, y luego la inicia desde la pantalla de inicio o el menú del iniciador, esa invocación posterior creará una NUEVA tarea ... efectivamente tendrá dos instancias diferentes de su aplicación habitando la misma pila ... lo cual se vuelve muy extraño muy rápido. Esto parece suceder cuando inicia su aplicación durante el desarrollo (es decir, desde Eclipse o Intellij), por lo que los desarrolladores se encuentran mucho con esto. 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 problema principal era este error, no el comportamiento del marco previsto. Una gran crítica ysolución alterna (ACTUALIZACIÓN: ver más abajo) parece ser del usuario @kaciula en esta respuesta:

Comportamiento de la tecla de inicio

ACTUALIZACIÓN Junio ​​de 2013 : meses después, finalmente encontré la solución 'correcta'. No es necesario que administre ningún indicador de aplicación de inicio con estado, puede detectarlo desde el marco y rescatarlo 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;
    }
}
Mike Repass
fuente
87

onSaveInstanceStatese llama 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 aplicación también debe ser guardado en onPauseDebe guardarse en algún almacenamiento persistente como PreferencesoSqlite

Fedor
fuente
36
Lo siento, eso no es del todo correcto. Se llama a onSaveInstanceState antes de que la actividad deba volver a realizarse. es decir, cada vez que el usuario gira el dispositivo. Está destinado a almacenar estados de vista transitorios. Cuando Android obliga a cerrar la aplicación, en realidad NO se llama a onSaveInstanceState (por lo que no es seguro para almacenar datos importantes de la aplicación). onPause, sin embargo, se garantiza que se llamará antes de que se elimine la actividad, por lo que se debe usar para almacenar información permanente en preferencias o Squlite. Respuesta correcta, razones equivocadas.
moveaway00
74

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 quiere retomar donde lo dejó - guardar y restaurar los datos del paquete (como los datos del estado de la aplicación) onSaveInstanceState()y onRestoreInstanceState()generalmente es adecuado.

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

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

David
fuente
70

El estado de ahorro es un error en el mejor de los casos. Si necesita guardar datos persistentes, simplemente use una base de datos SQLite . Android hace que sea MUY 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;
Mike A.
fuente
99
Debido a que lleva demasiado tiempo cargar una base de datos SQLite, teniendo en cuenta que se trata de la ruta crítica para mostrar al usuario la interfaz de usuario de la aplicación. En realidad no lo he cronometrado, así que estoy feliz de que me corrijan, pero ¿seguro que cargar y abrir un archivo de base de datos no será rápido?
Tom
55
¡Muchas gracias por proporcionar una solución que un novato puede cortar y pegar en su aplicación y usar de inmediato! @ Tom En cuanto a la velocidad, se necesitan unos siete segundos para almacenar 1000 pares, pero puede hacerlo en una AsyncTask. Sin embargo, debe agregar finalmente un {cursor.close ()} o se bloqueará debido a una pérdida de memoria al hacerlo.
Noumenon
3
Me encontré con esto y, aunque parece ordenado, dudo en intentar utilizar esto en Google Glass, que es el dispositivo con el que estoy trabajando últimamente.
Stephen Tetreault
61

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 la actividad1 a la actividad2 (he realizado algunos trabajos en la actividad2) y nuevamente a la actividad 1 haciendo clic en un botón en la actividad1. Ahora, en esta etapa, quería volver a la actividad2 y quiero ver mi actividad2 en la misma condición la última vez que dejé la actividad2.

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 actividad 1 en el evento de clic de botón, he hecho así:

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

Y en la actividad 2 al hacer clic en el evento de botón, he hecho así:

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

Ahora, lo que sucederá es que cualesquiera que sean los cambios que hemos realizado en la actividad2 no se perderán, y podemos ver la actividad2 en el mismo estado en que nos fuimos anteriormente.

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

Roy mathew
fuente
2
¿@bagusflyer quiere ser más específico? Su comentario no es útil y nadie puede ayudarlo en base a eso.
Stephen Tetreault
2
Esta es una respuesta a una situación diferente: dos actividades dentro de la misma aplicación. OP se trata de abandonar la aplicación (por ejemplo, el botón de inicio u otro medio para cambiar a una aplicación diferente).
ToolmakerSteve
44

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

Android llama a onSaveInstanceState () si se detiene la actividad 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 construyes 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 eso (con una llamada de acabado (), 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 tomar menos recursos cuando está en estado pasivo.

Ixx
fuente
40

Realmente onSaveInstanceState()se llama cuando la actividad pasa a segundo plano.

Cita de los documentos: "Este método se llama antes de que una actividad pueda ser eliminada para que cuando regrese en algún momento en el futuro pueda restaurar su estado". Fuente

u-foka
fuente
37

Para ayudar a reducir las repeticiones utilizo lo siguiente interfacey classpara leer / escribir en un Bundleestado de instancia para guardar.


Primero, cree una interfaz que se utilizará 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 .

Jared Rummler
fuente
34

Mientras tanto, en general no uso más

Bundle savedInstanceState & Co

El ciclo de vida es para la mayoría de las actividades demasiado complicado y no necesario.

Y Google se afirma a sí mismo, ni siquiera es confiable.

Mi forma es guardar cualquier cambio inmediatamente en las preferencias:

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

De alguna manera, SharedPreferences funciona de manera similar a los Bundles. Y, naturalmente, al principio, dichos valores deben leerse a partir de las preferencias.

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

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

stefan bachert
fuente
31

Para responder 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 de estado cuando:

  • Cambios de configuración, como cambiar la orientación o el idioma del teléfono, lo que puede requerir que se cree una nueva instancia de actividad.
  • Vuelve a la aplicación desde el fondo una vez que el sistema operativo ha 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 su ejemplo de hello world, hay algunas maneras de salir y volver a la Actividad.

  • Cuando presiona el botón Atrás, la actividad finaliza. 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 utiliza el conmutador de tareas, la Actividad pasará a segundo plano. Al navegar de regreso a la aplicación, solo se llamará a onCreate si la Actividad tuvo que ser destruida.

En la mayoría de los casos, si solo 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 mantener actividades". Cuando está habilitado, Android siempre destruirá actividades y las recreará cuando estén en segundo plano. Esta es una gran opción para dejarla habilitada cuando se desarrolla porque simula el peor de los casos. (Un dispositivo con poca memoria que recicla sus 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 funcionaba de la manera que esperaba.

Jared Kells
fuente
28

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

Mahorad
fuente
2
En su caso onCreate, y onRestoreInstanceStateno están siendo llamados porque el Activityno se destruye en absoluto cuando se cambia de aplicaciones, así que no hay necesidad de restaurar cualquier cosa. Android llama onSaveInstanceStatesolo en caso de que la Actividad se destruya más tarde (lo que sucede con 100% de certeza al rotar la pantalla porque la configuración completa del dispositivo ha cambiado y la Actividad debe volver a crearse desde cero).
Vicky Chijwani
20

Mi problema era que necesitaba persistencia solo durante la vida útil de la aplicación (es decir, una sola ejecución que incluye iniciar otras sub-actividades dentro de la misma aplicación y rotar el dispositivo, etc.). Intenté 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 al salvadoInstanceState durante onCreate:

mySavedInstanceState=savedInstanceState;

y usar eso para obtener el contenido de mi variable cuando lo necesite, en la línea de:

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

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

torwalker
fuente
19

Aunque la respuesta aceptada es correcta, existe un método más rápido y fácil para guardar el estado de la Actividad en Android usando una biblioteca llamada Icepick . Icepick es un procesador de anotaciones que se encarga de todo el código repetitivo 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 funcionará con cualquier objeto que guarde su estado con a Bundle.

kevinc
fuente
16

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 vuelve a crear. 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 un objeto de paquete "outState" como outState.putString ("clave", "Bienvenido de nuevo") y guarde llamando a super. Cuando se destruye la actividad, su estado se guarda 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");
    }
Mansuu ....
fuente
15

Básicamente, hay dos formas de implementar este cambio.

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

Realmente no recomiendo usar el segundo método. Dado que, según mi experiencia, estaba causando que la mitad de la pantalla del dispositivo se oscureciera al girar de vertical a horizontal y viceversa.

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

Ejemplo: considere un caso si desea persistir el objeto Json. crear una clase de modelo con captadores y establecedores.

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

}
Krishna
fuente
11

Aquí hay un comentario de la respuesta de Steve Moseley (por ToolmakerSteve ) que pone las cosas en perspectiva (en general, onSaveInstanceState 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 apoyarlo, para obtener recursos mínimos del sistema operativo, pero si una aplicación quiere devolver al usuario al estado en que se encontraba, sin importar cómo se cerró la aplicación, es necesario utilizar un enfoque de almacenamiento persistente. Uso onCreate para verificar el paquete y, si falta, verifique el almacenamiento persistente. Esto centraliza la toma de decisiones. Puedo recuperarme de un bloqueo, o salir del botón de retroceso o Salir del elemento de menú personalizado, o volver a la pantalla del usuario muchos días después. - ToolmakerSteve Sep 19 '15 a las 10:38

Sam es
fuente
10

Código de 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

Agregue valores predeterminados si no desea tener Opcionales

Rafols
fuente
9

Para obtener datos de estado de actividad almacenados onCreate(), primero debe guardar los datos en salvadoInstanceState anulandoSaveInstanceState(Bundle savedInstanceState) método.

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

ascii_walker
fuente
6

Simple rápido para resolver este problema es usar IcePick

Primero, configure 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 sobre cómo guardar el estado en 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 Paquete (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
}
THANN Phearum
fuente
1
@ralphspoon sí, funciona para Fragment y Custom View. Por favor verifique el código de ejemplo. Edité mi respuesta. Le sugiero que vaya a los documentos oficiales aquí github.com/frankiesardo/icepick para encontrar más ejemplos de código.
THANN Phearum
@ChetanMehra te refieres a la clase de vista personalizada, ¿verdad? Si es una vista personalizada, podemos anular onSaveInstanceState y onRestoreInstanceState como en el ejemplo anterior de CustomView.
THANN Phearum
Me refiero al objeto de clase dentro de la vista clase, por ejemplo: clase CustomView extiende Vista {@State ClassA a;} o clase CustomView extiende Vista {@ State Inner class {}}
Chetan Mehra
@ THANNPhearum ¿Debería formularlo como otra pregunta?
Chetan Mehra
Veo. Si es así, su Clase A debe ser Parcelable. Como mencionó que funciona para actividades, fragmentos o cualquier objeto que necesite serializar su estado en un paquete
THANN Phearum
6

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 lo recupera de una base de datos SQLite depende de sus requisitos. Esto es lo que hacen los servicios de cualquier tipo, proporcionan servicios tales como el mantenimiento del estado de la aplicación y la lógica empresarial común abstracta.

Debido a las limitaciones de memoria y procesamiento inherentes a los dispositivos móviles, trato las vistas de Android de forma 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 antiguo modelo de Modelo, Vista, Controlador (MVC), donde la página es la Vista, los datos de dominio son el modelo y el controlador se encuentra detrás de un servicio web. El mismo patrón puede emplearse en Android con la Vista siendo, bueno ... la Vista, el modelo son sus datos de dominio y el Controlador se implementa como un servicio vinculado a Android. Siempre que desee que una vista interactúe con el controlador, vincúlela al inicio / reanudar y desvincula al detener / pausar.

Este enfoque le brinda la ventaja adicional de hacer cumplir el principio de diseño de Separación de preocupaciones en el sentido de que toda la lógica empresarial de su aplicación puede trasladarse a su servicio, lo que reduce la lógica duplicada en varias vistas y permite que la vista imponga otro principio de diseño importante, la responsabilidad única.

Adelante
fuente
5

Kotlin

Debe anular onSaveInstanceStatey onRestoreInstanceStatealmacenar y recuperar las variables que desea que sean persistentes.

Gráfico del ciclo de vida

Almacenar variables

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

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

Recuperar variables

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

    val myInt = savedInstanceState.getInt("kInt")
    val myBoolean = savedInstanceState.getBoolean("kBool")
    val myDouble = savedInstanceState.getDouble("kDouble")
    val myString = savedInstanceState.getString("kString")
    // use variables here
}
Sazzad Hissain Khan
fuente
2

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

M Abdul Sami
fuente
3
Esto no es verdad. De la documentación: "A diferencia del estado de la instancia guardada, los ViewModels se destruyen durante la muerte de un proceso iniciado por el sistema. Es por eso que debe usar los objetos ViewModel en combinación con onSaveInstanceState () (o alguna otra persistencia de disco), ocultando los identificadores en savedInstanceState para ayudar a ver los modelos vuelven a cargar los datos después de la muerte del sistema ".
Vyacheslav Martynenko
Acabo de encontrar esto con los permisos cambiando en segundo plano.
Brill Pappin
Estoy de acuerdo, desde el documento "si necesita manejar la muerte del proceso iniciado por el sistema, puede usar onSaveInstanceState () como respaldo".
Zhar
2

Hay una manera de hacer que Android salve los estados sin implementar ningún método. Simplemente agregue esta línea a su Declaración de Manifiesto en Actividad:

android:configChanges="orientation|screenSize"

Debe tener un aspecto como este:

<activity
    android:name=".activities.MyActivity"
    android:configChanges="orientation|screenSize">
</activity>

Aquí puede encontrar más información sobre esta propiedad.

Se recomienda dejar que Android maneje esto por usted que el manejo manual.

IgniteCoders
fuente
2
Esto no tiene nada que ver con salvar el estado, solo renuncia a los cambios de orientación, tenga en cuenta que su aplicación se puede reiniciar, pausar y reanudar en cualquier momento para diferentes eventos
lord-ralf-adolf
1
Esta respuesta es para aquellos que quieren salvar el estado cuando la orientación cambia y quieren evitar la comprensión y la implementación de una manera compleja
IgniteCoders
bastante justo entiendo su punto, creo que la mayoría de las personas que luchan por salvar el estado están usando fragmentos porque las actividades realmente guardan estadísticas de los componentes de la interfaz de usuario siempre que tengan una identificación, pero los fragmentos son más especiales, usé fragmentos una vez pero nunca los usaré una vez más, la estadística de instancia de guardado fue difícil de manejar
lord-ralf-adolf
funciona ... gracias
Fanadez
1

¿Qué guardar y qué no?

Alguna vez se preguntó por qué el texto en el EditText se guarda automáticamente mientras cambia la orientación? Bueno, esta respuesta es para ti.

Cuando una instancia de una Actividad se destruye y el Sistema recrea una nueva instancia (por ejemplo, cambio de configuración). Intenta recrearlo utilizando un conjunto de datos guardados del estado de actividad anterior ( estado de instancia ).

El estado de instancia es una colección de pares clave-valor almacenados en un Bundleobjeto.

Por defecto, el Sistema guarda los objetos Ver en el Paquete, por ejemplo.

  • Texto en EditText
  • Posición de desplazamiento en a ListView, etc.

Si necesita guardar otra variable como parte del estado de instancia, debe ANULAR el onSavedInstanceState(Bundle savedinstaneState) método.

Por ejemplo, int currentScoreen una GameActivity

Más detalles sobre onSavedInstanceState (Bundle savedinstaneState) al guardar datos

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

Entonces, por error, si olvida llamar, super.onSaveInstanceState(savedInstanceState);el comportamiento predeterminado no funcionará, es decir, el texto en EditText no se guardará.

¿Cuál elegir para restaurar el estado de la actividad?

 onCreate(Bundle savedInstanceState)

O

onRestoreInstanceState(Bundle savedInstanceState)

Ambos métodos obtienen el mismo objeto Bundle, por lo que realmente no importa dónde escriba su lógica de restauración. La única diferencia es que, en el onCreate(Bundle savedInstanceState)método, deberá realizar una comprobación nula mientras no sea necesaria en este último caso. Otras respuestas ya tienen fragmentos de código. Puedes referirlos.

Más detalles sobre 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);
}

Siempre llame super.onRestoreInstanceState(savedInstanceState);para que el sistema restaure la jerarquía de vistas de forma predeterminada

Prima

El sistema onSaveInstanceState(Bundle savedInstanceState)lo invoca solo cuando el usuario tiene la intención de volver a la Actividad. Por ejemplo, está utilizando la aplicación X y de repente recibe una llamada. Se mueve a la aplicación de llamada y vuelve a la aplicación X. En este caso, elonSaveInstanceState(Bundle savedInstanceState) , se invocará método.

Pero considere esto si un usuario presiona el botón Atrás. Se supone que el usuario no tiene la intención de volver a la Actividad, por lo tanto, en este caso onSaveInstanceState(Bundle savedInstanceState)no será invocado por el sistema. Señale que debe considerar todos los escenarios al guardar los datos.

Enlaces relevantes:

Demostración sobre comportamiento predeterminado
Documentación oficial de Android .

Rohit Singh
fuente
1

Ahora tiene sentido hacer 2 formas en el modelo de vista. si desea guardar el primero como una instancia guardada: puede agregar el parámetro de estado en el modelo de vista como este https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate#java

o puede guardar variables u objetos en el modelo de vista, en este caso el modelo de vista mantendrá el ciclo de vida hasta que se destruya la actividad.

public class HelloAndroidViewModel extends ViewModel {
   public Booelan firstInit = false;

    public HelloAndroidViewModel() {
        firstInit = false;
    }
    ...
}

public class HelloAndroid extends Activity {

  private TextView mTextView = null;
  HelloAndroidViewModel viewModel = ViewModelProviders.of(this).get(HelloAndroidViewModel.class);
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    //Because even if the state is deleted, the data in the viewmodel will be kept because the activity does not destroy
    if(!viewModel.firstInit){
        viewModel.firstInit = true
        mTextView.setText("Welcome to HelloAndroid!");
    }else{
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}
Umut ADALI
fuente
tienes razón, pero esta biblioteca aún está en lanzamiento, así que creo que deberíamos esperar ...
Zhar