onSaveInstanceState () y onRestoreInstanceState ()

138

Estoy tratando de guardar y restaurar el estado de un Activityusando los métodos onSaveInstanceState()y onRestoreInstanceState().

El problema es que nunca ingresa al onRestoreInstanceState()método. ¿Alguien puede explicarme por qué es esto?

BlaBRA
fuente
1
@Nitin: gracias por compartir el enlace ... esto me ha aclarado algunas cosas +1
Taliadon el
2
@NitinBansal el enlace está muerto.
ashishdhiman2007

Respuestas:

191

Por lo general, restaura su estado onCreate(). Es posible restaurarlo onRestoreInstanceState()también, pero no es muy común. ( onRestoreInstanceState()se llama después onStart(), mientras que onCreate()se llama antes onStart().

Use los métodos put para almacenar valores en onSaveInstanceState():

protected void onSaveInstanceState(Bundle icicle) {
  super.onSaveInstanceState(icicle);
  icicle.putLong("param", value);
}

Y restaurar los valores en onCreate():

public void onCreate(Bundle icicle) {
  if (icicle != null){
    value = icicle.getLong("param");
  }
}
Robert
fuente
2
El problema es que uso startActivity para volver a la actividad A. Al volver a la actividad B, el objeto es un carámbano nulo.
BlaBRA
55
Si entiendo correctamente, esto es lo que está haciendo: desde B llama a startActivity (A). Luego, desde A, llamas a acabado () para volver a B. ¿Verdad? En ese caso, su primera actividad, B no se habrá destruido, ni se llamará a onCreate () ni a onRestoreInstanceState (). Estos métodos solo se llaman cuando es necesario, es decir, cuando una actividad ha sido destruida y necesita ser recreada por el sistema.
Robert
44
Debo agregar que su primera actividad, B, podría destruirse debido a condiciones de poca memoria. Esto activará onCreate y onRestoreInstanceState.
Robert
1
erikb, sí, la actividad B se reanudará o, en caso de que el sistema operativo la haya reclamado, se volverá a crear y luego se reanudará.
Robert
1
Oh, esta es la razón
Al Lelopath
149

onRestoreInstanceState()solo se llama cuando se recrea la actividad después de que el sistema operativo la eliminó . Tal situación ocurre cuando:

  • la orientación del dispositivo cambia (su actividad se destruye y se recrea).
  • hay otra actividad frente a la suya y en algún momento el sistema operativo mata su actividad para liberar memoria (por ejemplo). La próxima vez que comience su actividad, se llamará a RestoreInstanceState ().

Por el contrario: si está en su actividad y Backpresiona el botón en el dispositivo, su actividad se termina () edita (es decir, piensa que es una aplicación de escritorio existente) y la próxima vez que inicie su aplicación, se iniciará "fresca", es decir, sin estado guardado porque lo saliste intencionalmente cuando golpeaste Back.

Otra fuente de confusión es que cuando una aplicación pierde el foco onSaveInstanceState()se llama a otra aplicación, pero cuando navegas de regreso a tu aplicación, es onRestoreInstanceState()posible que no se llame. Este es el caso descrito en la pregunta original, es decir, si su actividad NO se eliminó durante el período en que otra actividad estaba al frente onRestoreInstanceState()NO se llamará porque su actividad está prácticamente "viva".

En general, como se indica en la documentación para onRestoreInstanceState():

La mayoría de las implementaciones simplemente usarán onCreate (Bundle) para restaurar su estado, pero a veces es conveniente hacerlo aquí después de que se haya realizado toda la inicialización o para permitir que las subclases decidan si usarán su implementación predeterminada. La implementación predeterminada de este método realiza una restauración de cualquier estado de vista que previamente haya sido congelado por onSaveInstanceState (Bundle).

Mientras lo leo: no hay razón para anular a onRestoreInstanceState()menos que esté subclasificando Activityy se espera que alguien subclasifique su subclase.

Ognyan
fuente
3
yeh, esto parece estar bien, pero apesta. OMI también debe ejecutarse al volver a la actividad desde otra actividad. Hay muchas situaciones en las que necesita esto.
masi
44
@masi ya hay otros métodos invocados en Activity cuando el usuario vuelve a ella (desde otra actividad). OnSave / RestoreInstanceState () se usa para otro propósito específico, eso es todo.
superjos
8

El estado en el que guarda onSaveInstanceState()está disponible más tarde en la onCreate()invocación del método. Entonces use onCreate(y su Bundleparámetro) para restaurar el estado de su actividad.

Konstantin Burov
fuente
4

Como solución alternativa, puede almacenar un paquete con los datos que desea mantener en la intención que utiliza para iniciar la actividad A.

Intent intent = new Intent(this, ActivityA.class);
intent.putExtra("bundle", theBundledData);
startActivity(intent);

La Actividad A tendría que devolver esto a la Actividad B. Recuperaría la intención en el método onCreate de la Actividad B.

Intent intent = getIntent();
Bundle intentBundle;
if (intent != null)
    intentBundle = intent.getBundleExtra("bundle");
// Do something with the data.

Otra idea es crear una clase de repositorio para almacenar el estado de la actividad y hacer que cada una de sus actividades haga referencia a esa clase (posible utilizando una estructura singleton). Sin embargo, hacerlo probablemente sea más problemático de lo que vale.

sotrh
fuente
3

Lo principal es que si no almacena en, onSaveInstanceState()entonces onRestoreInstanceState()no será llamado. Esta es la principal diferencia entre restoreInstanceState()y onCreate(). Asegúrate de guardar algo realmente. Lo más probable es que este sea tu problema.

usuario1771286
fuente
1
Se llamará a onRestoreInstanceState (), incluso si no almacena nada en OnSaveInstanceState ()
abh22ishek
3

Descubrí que onSaveInstanceState siempre se llama cuando otra actividad aparece en primer plano. Y también lo es onStop.

Sin embargo, onRestoreInstanceState solo se llamó cuando onCreate y onStart también se llamaron. Y, onCreate y onStart NO siempre se llamaban.

Por lo tanto, parece que Android no siempre elimina la información de estado, incluso si la Actividad se mueve a un segundo plano. Sin embargo, llama a los métodos del ciclo de vida para guardar el estado solo para estar seguro. Por lo tanto, si el estado no se elimina, Android no llama a los métodos del ciclo de vida para restaurar el estado, ya que no son necesarios.

La figura 2 describe esto.

zumbido
fuente
2

Creo que este hilo era bastante viejo. Solo menciono otro caso, que onSaveInstanceState()también se llamará, es cuando llamas Activity.moveTaskToBack(boolean nonRootActivity).

macio.Jun
fuente
1

Si está manejando los cambios de orientación de la actividad con android:configChanges="orientation|screenSize"y onConfigurationChanged(Configuration newConfig), onRestoreInstanceState()no se llamará.

Rajkiran
fuente
1

No es necesario que onRestoreInstanceState siempre se llame después de onSaveInstanceState.

Tenga en cuenta que: onRestoreInstanceState siempre se llamará, cuando se gira la actividad (cuando no se maneja la orientación) o abra su actividad y luego abra otras aplicaciones para que el sistema operativo borre su instancia de actividad de la memoria.

Ashutosh Srivastava
fuente
1

De la documentación Restaurar el estado de la IU de la actividad utilizando el estado de instancia guardado se indica como:

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

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

En mi opinión, esta es una forma más clara que verificar esto en onCreate, y se ajusta mejor con el principio de responsabilidad única.

Teoman shipahi
fuente
0

En mi caso, onRestoreInstanceStatese llamó cuando la actividad se reconstruyó después de cambiar la orientación del dispositivo. onCreate(Bundle)se llamó primero, pero el paquete no tenía la clave / los valores que configuré onSaveInstanceState(Bundle).

Justo después, onRestoreInstanceState(Bundle)fue llamado con un paquete que tenía la clave / valores correctos.

elvitucho
fuente
0

Puedo hacer eso (lo siento, es C #, no Java, pero no es un problema ...):

private int iValue = 1234567890;

function void MyTest()
{
    Intent oIntent = new Intent (this, typeof(Camera2Activity));
    Bundle oBundle = new Bundle();
    oBundle.PutInt("MYVALUE", iValue); //=> 1234567890
    oIntent.PutExtras (oBundle);
    iRequestCode = 1111;
    StartActivityForResult (oIntent, 1111);
}

Y EN TU ACTIVIDAD PARA EL RESULTADO

private int iValue = 0;

protected override void OnCreate(Bundle bundle)
{
    Bundle oBundle =  Intent.Extras;
    if (oBundle != null)
    {
        iValue = oBundle.GetInt("MYVALUE", 0);
        //=>1234567890
    }
}

private void FinishActivity(bool bResult)
{
    Intent oIntent = new Intent();
    Bundle oBundle = new Bundle();
    oBundle.PutInt("MYVALUE", iValue);//=>1234567890
    oIntent.PutExtras(oBundle);
    if (bResult)
        {
            SetResult (Result.Ok, oIntent);
        }
    else
        SetResult(Result.Canceled, oIntent);
    GC.Collect();
    Finish();
}

FINALMENTE

protected override void OnActivityResult(int iRequestCode, Android.App.Result oResultCode, Intent oIntent)
{
    base.OnActivityResult (iRequestCode, oResultCode, oIntent);
    iValue = oIntent.Extras.GetInt("MYVALUE", -1); //=> 1234567890
}
EDynamic90
fuente