obteniendo la excepción "IllegalStateException: no se puede realizar esta acción después de onSaveInstanceState"

Resuelto dcool asked hace 54 años • 0 respuestas

Tengo una aplicación Live Android y del mercado recibí el siguiente seguimiento de pila y no tengo idea de por qué sucede, ya que no sucede en el código de la aplicación, sino que es causado por algún evento de la aplicación (suposición).

No estoy usando Fragmentos, todavía hay una referencia de FragmentManager. Si alguien puede arrojar algo de luz sobre algunos hechos ocultos para evitar este tipo de problemas:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyDown(Activity.java:1962)
at android.view.KeyEvent.dispatch(KeyEvent.java:2482)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1720)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1258)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2851)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2824)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2011)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4025)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)  
dcool avatar Jan 01 '70 08:01 dcool
Aceptado

Este es el error más estúpido que he encontrado hasta ahora. Tenía una Fragmentaplicación que funcionaba perfectamente para API <11 y Force Closingpara API> 11 .

Realmente no pude entender qué cambiaron dentro del Activityciclo de vida en la llamada a saveInstance, pero así es como resolví esto:

@Override
protected void onSaveInstanceState(Bundle outState) {
    //No call for super(). Bug on API Level > 11.
}

Simplemente no hago la llamada .super()y todo funciona muy bien. Espero que esto te ahorre algo de tiempo.

EDITAR: después de investigar un poco más, este es un error conocido en el paquete de soporte.

Si necesita guardar la instancia y agregar algo, outState Bundlepuede usar lo siguiente:

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
    super.onSaveInstanceState(outState);
}

EDITAR2: esto también puede ocurrir si está intentando realizar una transacción después de que ya Activityno esté en segundo plano. Para evitar esto debes usarcommitAllowingStateLoss()

EDITAR3: Las soluciones anteriores solucionaban problemas en las primeras bibliotecas support.v4, por lo que puedo recordar. Pero si aún tiene problemas con esto, DEBE leer también el blog de @AlexLockwood : Transacciones de fragmentos y pérdida de estado de actividad.

Resumen de la publicación del blog (pero te recomiendo encarecidamente que lo leas):

  • NUNCA realice commit() transacciones después onPause()de antes y onStop()después de Honeycomb
  • Tenga cuidado al realizar transacciones dentro de Activitymétodos de ciclo de vida. uso onCreate() y onResumeFragments()_onPostResume()
  • Evite realizar transacciones dentro de métodos de devolución de llamada asíncronos
  • Úselo commitAllowingStateLoss()solo como último recurso
Ovidiu Latcu avatar Apr 21 '2012 17:04 Ovidiu Latcu

Al buscar en el código fuente de Android las causas de este problema, el indicador mStateSaved en FragmentManagerImplla clase (instancia disponible en Actividad) tiene el valor verdadero. Se establece en verdadero cuando se guarda la pila de actividades (saveAllState) en una llamada desde Activity#onSaveInstanceState. Posteriormente, las llamadas de ActivityThread no restablecen este indicador utilizando los métodos de restablecimiento disponibles desde FragmentManagerImpl#noteStateNotSaved()y dispatch().

A mi modo de ver, hay algunas correcciones disponibles, dependiendo de lo que esté haciendo y usando su aplicación:

buenas maneras

Antes que nada: anunciaría el artículo de Alex Lockwood . Entonces, por lo que he hecho hasta ahora:

  1. Para fragmentos y actividades que no necesitan conservar ninguna información de estado, llame a commitAllowStateLoss . Tomado de la documentación:

    Permite que la confirmación se ejecute después de guardar el estado de una actividad. Esto es peligroso porque la confirmación se puede perder si la actividad necesita ser restaurada posteriormente desde su estado, por lo que esto solo debe usarse en casos en los que está bien que el estado de la interfaz de usuario cambie inesperadamente en el usuario. Supongo que está bien usarlo si el fragmento muestra información de solo lectura. O incluso si muestran información editable, utilice los métodos de devolución de llamada para conservar la información editada.

  2. Justo después de confirmar la transacción (acabas de llamar commit()), haz una llamada a FragmentManager.executePendingTransactions().

Formas no recomendadas:

  1. Como Ovidiu Latcu mencionó anteriormente, no llames super.onSaveInstanceState(). Pero esto significa que perderá todo el estado de su actividad junto con el estado de los fragmentos.

  2. Anular onBackPressedy allí llamar sólo finish(). Esto debería estar bien si su aplicación no usa Fragments API; como en super.onBackPressedhay una llamada a FragmentManager#popBackStackImmediate().

  3. Si está utilizando la API de Fragmentos y el estado de su actividad es importante/vital, entonces podría intentar llamar usando la API de reflexión FragmentManagerImpl#noteStateNotSaved(). Pero esto es un truco, o se podría decir que es una solución alternativa. No me gusta, pero en mi caso es bastante aceptable ya que tengo un código de una aplicación heredada que usa código obsoleto ( TabActivitye implícitamente LocalActivityManager).

A continuación se muestra el código que utiliza la reflexión:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    invokeFragmentManagerNoteStateNotSaved();
}

@SuppressWarnings({ "rawtypes", "unchecked" })
private void invokeFragmentManagerNoteStateNotSaved() {
    /**
     * For post-Honeycomb devices
     */
    if (Build.VERSION.SDK_INT < 11) {
        return;
    }
    try {
        Class cls = getClass();
        do {
            cls = cls.getSuperclass();
        } while (!"Activity".equals(cls.getSimpleName()));
        Field fragmentMgrField = cls.getDeclaredField("mFragments");
        fragmentMgrField.setAccessible(true);

        Object fragmentMgr = fragmentMgrField.get(this);
        cls = fragmentMgr.getClass();

        Method noteStateNotSavedMethod = cls.getDeclaredMethod("noteStateNotSaved", new Class[] {});
        noteStateNotSavedMethod.invoke(fragmentMgr, new Object[] {});
        Log.d("DLOutState", "Successful call for noteStateNotSaved!!!");
    } catch (Exception ex) {
        Log.e("DLOutState", "Exception on worka FM.noteStateNotSaved", ex);
    }
}

¡Salud!

gunar avatar Dec 21 '2012 09:12 gunar

Se producirá una excepción de este tipo si intenta realizar una transición de fragmento después de que onSaveInstanceState()se llame a su actividad de fragmento.

Una de las razones por las que esto puede suceder es si deja un AsyncTask(o Thread) en ejecución cuando se detiene una actividad.

Cualquier transición posterior onSaveInstanceState()a la llamada podría perderse si el sistema reclama la actividad para obtener recursos y la recrea más tarde.

FunkTheMonk avatar Nov 09 '2011 00:11 FunkTheMonk

Simplemente llame a super.onPostResume() antes de mostrar su fragmento o mueva su código en el método onPostResume() después de llamar a super.onPostResume(). ¡Esto resuelve el problema!

 avatar Sep 16 '2013 09:09

Esto también puede suceder cuando se llama dismiss()a un fragmento de diálogo después de que la pantalla se haya bloqueado/blanqueado y se haya guardado el estado de la instancia del diálogo Actividad +. Para evitar esta llamada:

dismissAllowingStateLoss()

Literalmente, cada vez que descarto un diálogo ya no me importa su estado, así que está bien hacerlo: en realidad no estás perdiendo ningún estado.

Brian Dilley avatar Oct 22 '2013 23:10 Brian Dilley