IllegalStateException: No se puede realizar esta acción después de onSaveInstanceState con ViewPager

Estoy recibiendo informes de usuarios de mi aplicación en el mercado, entregando 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. El stacktrace no muestra ninguna de mis propias clases, por lo que no tengo idea de dónde se produce esta excepción y cómo evitarlo.

Para el registro: Tengo un tabhost, y en cada una de las pestañas hay un cambio de ActivityGroup entre Actividades.

Por favor, compruebe mi respuesta aquí . Básicamente, sólo tenía que:

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

No realice la llamada a super() en el método saveInstanceState . Esto estaba estropeando las cosas …

Este es un error conocido en el paquete de soporte.

Si necesita guardar la instancia y agregar algo a su Bundle outState , puede utilizar 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) para usar:

 transaction.commitAllowingStateLoss(); 

Al agregar o realizar el FragmentTransaction que estaba causando la Exception .

Hay muchos problemas relacionados con un mensaje de error similar. Compruebe la segunda línea de este rastreo de pila en particular. Esta excepción está específicamente relacionada con la llamada a FragmentManagerImpl.popBackStackImmediate .

Esta llamada al método, como popBackStack , siempre fallará con IllegalArgumentException si el estado de la sesión ya se ha guardado. Compruebe la fuente. No hay nada que puedas hacer para detener esta excepción.

  • Eliminar la llamada a super.onSaveInstanceState no ayudará.
  • Crear el fragmento con commitAllowingStateLoss no ayudará.

He aquí cómo observé el problema:

  • Hay un formulario con un botón 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 finalizar el proceso – onSaveInstanceState se llama.
  • El proceso se completa, se realiza una devolución de llamada y se intenta popBackStackImmediate .
  • IllegalStateException .

Esto es lo que hice para solucionarlo:

Como no es posible evitar la IllegalStateException en la devolución de llamada, capturarla e ignorarla.

 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 detener la caída de la aplicación. Pero ahora el usuario va a restaurar la aplicación y ver que el botón que pensaron que habían presionado no se ha presionado en absoluto (que piensan). El fragmento de formulario sigue mostrando!

Para corregir esto, cuando se crea el diálogo, haga 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 te olvides de cargar de nuevo en onViewCreated

Luego, al reanudar, revertir los fragmentos si el envío se intentó 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); } } 

Compruebe si la actividad isFinishing() antes de mostrar el fragmento y preste atención a 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(); } 

Aquí hay una solución diferente a este problema.

Utilizando una variable de miembro privado, puede establecer los datos devueltos como una intención que puede procesarse después de super.onResume ();

Al igual que:

 private Intent mOnActivityResultIntent = null; @Override protected void onResume() { super.onResume(); if(mOnActivityResultIntent != null){ ... do things ... mOnActivityResultIntent = null; } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data){ if(data != null){ mOnActivityResultIntent = data; } } 

Corto y trabajando Solución:

Sigue pasos sencillos

Pasos

Paso 1: onSaveInstanceState estado onSaveInstanceState en el fragmento respectivo. Y eliminar el método super de ella.

  @Override public void onSaveInstanceState( Bundle outState ) { } 

Paso 2: Use fragmentTransaction.commitAllowingStateLoss( );

En lugar de fragmentTransaction.commit( ); Mientras que las operaciones de fragmento.

Encontré una solución sucia para este tipo de problema. Si todavía desea mantener su ActivityGroups por cualquier razón (tuve razones de limitación de tiempo), sólo implementar

 public void onBackPressed() {} 

En su Activity y hacer un poco de código back allí. Incluso si no hay tal método en dispositivos más antiguos, este método obtiene llamado por los más nuevos.

BEWARE , using transaction.commitAllowingStateLoss() podría resultar en una mala experiencia para el usuario. Para obtener más información sobre por qué se produce esta excepción, consulte esta publicación .

Tuve un problema similar, el escenario era así:

  • Mi actividad está agregando / reemplazando fragmentos de la lista.
  • Cada fragmento de lista tiene una referencia a la actividad, para notificar la actividad cuando se hace clic en un elemento de lista (patrón de observador).
  • Cada fragmento de lista llama a setRetainInstance (true); En su método onCreate .

El método onCreate de la actividad fue así:

 mMainFragment = (SelectionFragment) getSupportFragmentManager() .findFragmentByTag(MAIN_FRAGMENT_TAG); if (mMainFragment == null) { mMainFragment = new SelectionFragment(); mMainFragment.setListAdapter(new ArrayAdapter<String>(this, R.layout.item_main_menu, getResources().getStringArray( R.array.main_menu))); mMainFragment.setOnSelectionChangedListener(this); FragmentTransaction transaction = getSupportFragmentManager() .beginTransaction(); transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG); transaction.commit(); } 

La excepción se lanzó porque cuando se cambia la configuración (dispositivo girado), se crea la actividad, se recupera el fragmento principal del historial del gestor de fragmentos y al mismo tiempo el fragmento ya tiene una referencia VIEJA a la actividad destruida

Cambiando la implementación a esto resolvió el problema:

 mMainFragment = (SelectionFragment) getSupportFragmentManager() .findFragmentByTag(MAIN_FRAGMENT_TAG); if (mMainFragment == null) { mMainFragment = new SelectionFragment(); mMainFragment.setListAdapter(new ArrayAdapter<String>(this, R.layout.item_main_menu, getResources().getStringArray( R.array.main_menu))); FragmentTransaction transaction = getSupportFragmentManager() .beginTransaction(); transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG); transaction.commit(); } mMainFragment.setOnSelectionChangedListener(this); 

Debe configurar sus oyentes cada vez que se cree la actividad para evitar la situación en la que los fragmentos tienen referencias a las antiguas instancias destruidas de la actividad.

No utilice commitAllowingStateLoss (), sólo debe utilizarse para los casos en que está bien que el estado de la interfaz de usuario cambie inesperadamente en el usuario.

https://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStateLoss ()

En su lugar, use if (fragment.isResume ()) check fuera de la operación que encontró esta IllegalStateException "No se puede realizar esta acción después onSaveInstanceState"

Estaba recibiendo esta excepción cuando estaba presionando el botón Atrás para cancelar el selector de intenciones en mi actividad de fragmento de mapa. Resolví esto reemplazando el código de onResume (donde estaba inicializando el fragmento) a onstart () y la aplicación está funcionando bien. Espero que ayude.

Creo que usando transaction.commitAllowingStateLoss(); No es la mejor solución. Esta excepción se activará cuando la configuración de la actividad cambie y se onSavedInstanceState() fragmento onSavedInstanceState() y posteriormente su método de devolución de llamada asíncrona intente confirmar el fragmento.

Solución simple podría ser comprobar si la actividad está cambiando la configuración o no

Por ejemplo, check isChangingConfigurations()

es decir

if(!isChangingConfigurations()) { //commit transaction. }

Revise este enlace también

Agregue esto en su actividad

 @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (outState.isEmpty()) { // Work-around for a pre-Android 4.2 bug outState.putBoolean("bug:fix", true); } } 

A partir de la versión de la biblioteca de soporte técnico 24.0.0 puede llamar al método FragmentTransaction.commitNow() que confirma esta transacción de forma síncrona en lugar de llamar a commit() seguido de executePendingTransactions() . Como la documentación dice que este enfoque aún mejor:

Llamar commitNow es preferible a llamar a commit () seguido de executePendingTransactions () ya que este último tendrá el efecto secundario de intentar confirmar todas las transacciones actualmente pendientes, ya sea que se trate del comportamiento deseado o no.

También he experimentado este problema y el problema se produce cada vez que el contexto de su FragmentActivity se cambia (por ejemplo, la orientación de la pantalla se cambia, etc). Así que la mejor solución es actualizar el contexto de tu FragmentActivity .

Posiblemente la solución más lisa y más simple que encontré en mi caso fue evitar que el fragmento ofensivo fuera de la pila en respuesta al resultado de la actividad. Cambiando tan esta llamada en mi onActivityResult() :

 popMyFragmentAndMoveOn(); 

a esto:

 new Handler(Looper.getMainLooper()).post(new Runnable() { public void run() { popMyFragmentAndMoveOn(); } } 

Ayudado en mi caso.

La excepción se lanza aquí (En FragmentActivity):

 @Override public void onBackPressed() { if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) { super.onBackPressed(); } } 

En FragmentManager.popBackStatckImmediate() , FragmentManager.checkStateLoss() se llama en primer lugar. Esa es la causa de IllegalStateException . Vea la implementación a continuación:

 private void checkStateLoss() { if (mStateSaved) { // Boom! throw new IllegalStateException( "Can not perform this action after onSaveInstanceState"); } if (mNoTransactionsBecause != null) { throw new IllegalStateException( "Can not perform this action inside of " + mNoTransactionsBecause); } } 

Resuelvo este problema simplemente usando una bandera para marcar el estado actual de la actividad. Aquí está mi solución:

 public class MainActivity extends AppCompatActivity { /** * A flag that marks whether current Activity has saved its instance state */ private boolean mHasSaveInstanceState; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override protected void onSaveInstanceState(Bundle outState) { mHasSaveInstanceState = true; super.onSaveInstanceState(outState); } @Override protected void onResume() { super.onResume(); mHasSaveInstanceState = false; } @Override public void onBackPressed() { if (!mHasSaveInstanceState) { // avoid FragmentManager.checkStateLoss()'s throwing IllegalStateException super.onBackPressed(); } } 

}

Una solución sencilla y compacta podría ser:

 @Override public void show(FragmentManager manager, String tag){ FragmentTransaction ft=manager.beginTransaction(); ft.add(this,tag); ft.commitAllowingStateLoss(); } 
  • Onclick en paginador de vista en android no funciona en mi código
  • "La actividad ha sido destruida" a veces cuando se puebla viewpager con fragmentos
  • Llame a actvity después de que el viewpager haya terminado
  • Error "java.lang.IllegalStateException: No se puede cambiar la etiqueta de fragmento" cuando se utiliza ViewPager # setCurrentItem ()
  • Posición de PagerTabStrip dentro de ViewPager
  • Fragmentos de viewpager anidados en android 2.3.3 y 4.0
  • Fragmento con ViewPager dentro de Fragmento y FragmentStatePagerAdapter resultados en Excepción (con ejemplo completo)
  • Cómo reemplazar fragmento a otro fragmento en TabLayout y ViewPager
  • Fragmento de elementos compartidos no funcionan con ViewPager
  • FragmentPagerAdapter y FragmentStatePagerAdapter
  • SearchView con múltiples fragmentos usando viewpager en android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.