IllegalStateException: no se puede realizar esta acción después de onSaveInstanceState con ViewPager
Recibo informes de usuarios de mi aplicación en el mercado, lo que genera la siguiente excepción:
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.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
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:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
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.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
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:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
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:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)
Aparentemente tiene algo que ver con un FragmentManager, que no uso. Stacktrace no muestra ninguna de mis clases, por lo que no tengo idea de dónde ocurre esta excepción y cómo evitarla.
Para que conste: tengo un tabhost y en cada pestaña hay un grupo de actividades que cambia entre actividades.
Por favor revisa mi respuesta aquí . Básicamente solo tenía que:
@Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
No realice la llamada super()
al saveInstanceState
método. Esto estaba arruinando las cosas...
Este es un error conocido en el paquete de soporte.
Si necesita guardar la instancia y agregar algo, outState
Bundle
puede usar lo siguiente:
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
super.onSaveInstanceState(outState);
}
Al final, la solución adecuada fue (como se ve en los comentarios) utilizar:
transaction.commitAllowingStateLoss();
al agregar o realizar el FragmentTransaction
que estaba causando el Exception
.
Hay muchos problemas relacionados con un mensaje de error similar. Verifique la segunda línea de este seguimiento de pila en particular. Esta excepción está específicamente relacionada con la llamada a FragmentManagerImpl.popBackStackImmediate
.
Esta llamada a método, como popBackStack
, siempre fallará IllegalStateException
si el estado de la sesión ya se ha guardado. Comprueba la fuente. No hay nada que pueda hacer para evitar que se lance esta excepción.
- Eliminar la llamada a
super.onSaveInstanceState
no ayudará. - Crear el Fragmento
commitAllowingStateLoss
no ayudará.
Así es como observé el problema:
- Hay un formulario con un botón de enviar.
- Cuando se hace clic en el botón, se crea un cuadro de diálogo y se inicia un proceso asíncrono.
- El usuario hace clic en la tecla de inicio antes de que finalice el proceso:
onSaveInstanceState
se le llama. - El proceso se completa, se realiza una devolución de llamada y
popBackStackImmediate
se intenta. IllegalStateException
es aventado.
Esto es lo que hice para resolverlo:
Como no es posible evitarlo IllegalStateException
en la devolución de llamada, cógelo e ignóralo.
try {
activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
// There's no way to avoid getting this if saveInstanceState has already been called.
}
Esto es suficiente para evitar que la aplicación falle. Pero ahora el usuario restaurará la aplicación y verá que el botón que pensó que había presionado no ha sido presionado en absoluto (cree). ¡El fragmento del formulario todavía se muestra!
Para solucionar este problema, cuando se cree el cuadro de diálogo, establezca algún estado para indicar que el proceso ha comenzado.
progressDialog.show(fragmentManager, TAG);
submitPressed = true;
Y guarde este estado en el paquete.
@Override
public void onSaveInstanceState(Bundle outState) {
...
outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}
No olvides volver a cargarlo enonViewCreated
Luego, al reanudar, revierta los fragmentos si se intentó enviar previamente. Esto evita que el usuario vuelva a lo que parece un formulario no enviado.
@Override
public void onResume() {
super.onResume();
if (submitPressed) {
// no need to try-catch this, because we are not in a callback
activity.getSupportFragmentManager().popBackStackImmediate(name);
submitPressed = false;
}
}
Compruebe si la actividad isFinishing()
antes de mostrar el fragmento y preste atención commitAllowingStateLoss()
.
Ejemplo:
if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
DummyFragment dummyFragment = DummyFragment.newInstance();
ft.add(R.id.dummy_fragment_layout, dummyFragment);
ft.commitAllowingStateLoss();
}